diff --git a/.gitignore b/.gitignore index 1205c5b..ddd3306 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ .idea .vscode -appchain main data/ config.yaml pelacli.yaml +CLAUDE.md diff --git a/Makefile b/Makefile index 99c9166..8b06f4c 100644 --- a/Makefile +++ b/Makefile @@ -16,14 +16,16 @@ dockerbuild: DOCKER_BUILDKIT=1 docker build --ssh default -t appchain:latest . up: - @echo "πŸ”Ό Starting containers..." docker compose up -d +up-monitoring: + docker compose --profile monitoring up -d + build: DOCKER_BUILDKIT=1 docker compose build --ssh default down: - docker compose down + docker compose --profile monitoring down logs: docker compose logs diff --git a/README.md b/README.md new file mode 100644 index 0000000..9ef5608 --- /dev/null +++ b/README.md @@ -0,0 +1,318 @@ +# Pelagos Bridge + +> A production-ready cross-chain bridge enabling seamless token transfers between L1 and L2 networks using the Pelagos SDK. + +[![Status](https://img.shields.io/badge/status-production%20ready-brightgreen)]() +[![License](https://img.shields.io/badge/license-MIT-blue)]() + +## Overview + +Pelagos Bridge is a cross-chain bridge that enables fast, secure token transfers between Ethereum L1 (Sepolia) and custom L2 networks (Stavanger). Built on the Pelagos SDK, it provides a simple, efficient bridging solution with validator-based consensus. + +### Key Features + +- βœ… **Bidirectional bridging** - L1 ↔ L2 token transfers +- βœ… **One-transaction bridging** - EIP-2612 permit support (gasless approval) +- βœ… **Automatic token mapping** - Seamless ERC20 ↔ native token conversion +- βœ… **Fast finality** - No ZK proof generation, validator-based consensus +- βœ… **Production frontend** - Modern UI with MetaMask integration +- βœ… **Event-driven architecture** - Real-time bridge transaction processing + +## Architecture + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Bridge Appchain β”‚ +β”‚ β€’ Monitors external chain events (pelacli) β”‚ +β”‚ β€’ Processes BridgeInitiated events β”‚ +β”‚ β€’ Generates ExternalTransactions for destination β”‚ +β”‚ β€’ Coordinates validator consensus β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬ + β”‚ β”‚ β”‚ +β”Œβ”€β”€β”€β–Όβ”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β” +β”‚Sepolia β”‚ β”‚Stavangerβ”‚ β”‚ Future β”‚ +β”‚ L1 β”‚ β”‚ L2 β”‚ β”‚ Chains β”‚ +β”‚Bridge β”‚ β”‚ Bridge β”‚ β”‚ ... β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +### How It Works + +1. **User initiates bridge** - Calls `bridgeAsset()` on source chain +2. **Token locked** - Tokens locked in source Bridge contract +3. **Event emitted** - `BridgeInitiated` event with bridge details +4. **Appchain processes** - Detects event, maps token addresses +5. **ExternalTransaction created** - Generates mint transaction for destination +6. **Destination mints** - Bridge contract releases/mints tokens to recipient + +## Deployed Contracts + +| Network | Chain ID | Bridge Contract | Explorer | +|---------|----------|----------------|----------| +| Sepolia (L1) | 11155111 | `0x844E740Ea7F404c6208fd85Ee6114a14F8037df7` | [Etherscan](https://sepolia.etherscan.io/address/0x844E740Ea7F404c6208fd85Ee6114a14F8037df7) | +| Stavanger (L2) | 50591822 | `0x3C1c8351a09DB0300786148B56EcB7be2FaA322e` | [Blockscout](https://explorer.stavanger.gateway.fm/address/0x3C1c8351a09DB0300786148B56EcB7be2FaA322e) | + +## Quick Start + +### Prerequisites + +- Go 1.25+ +- Docker & Docker Compose + +### Running the Bridge + +1. **Clone the repository** + ```bash + git clone + cd bridge + ``` + +2. **Configure environment** + ```bash + # Edit config files with your RPC keys and private keys + vim config.yaml # Appchain configuration + vim pelacli.yaml # Pelacli configuration (RPC URLs, private keys) + ``` + +3. **Start the bridge stack** + ```bash + docker compose up -d + ``` + OR + ```bash + make up + ``` + +4. **Access the UI** + - Frontend: http://localhost:3000 + - Explorer: http://localhost:3001 + +### Testing the Bridge + +#### Using the Frontend + +1. Open http://localhost:3000 +2. Connect MetaMask +3. Select source and destination networks +4. Enter amount and recipient +5. Click "Bridge with Permit" (one transaction) + + + +## Project Structure + +``` +bridge/ +β”œβ”€β”€ application/ # Core bridge logic +β”‚ β”œβ”€β”€ external_block_processor.go # Event processing & ExternalTransaction generation +β”‚ β”œβ”€β”€ subscription.go # Bridge contract subscriptions +β”‚ β”œβ”€β”€ bridge_event.go # Bridge event data types +β”‚ β”œβ”€β”€ state.go # Database query functions +β”‚ └── api/ # JSON-RPC API endpoints +β”œβ”€β”€ cmd/ # Application entry point +β”‚ └── main.go +β”œβ”€β”€ config.yaml # Appchain configuration +β”œβ”€β”€ pelacli.yaml # Pelacli configuration +β”œβ”€β”€ contracts/ # Solidity contracts +β”‚ └── contracts/ +β”‚ └── Bridge.sol +└── frontend/ # Web UI + β”œβ”€β”€ index.html + β”œβ”€β”€ app.js + └── styles.css +``` + +## Token Mapping + +The bridge automatically maps token addresses between chains: + +| Source Chain | Source Token | Destination Chain | Destination Token | +|--------------|--------------|-------------------|-------------------| +| Sepolia (L1) | POL ERC20 (`0x6a7c...`) | Stavanger (L2) | Native POL (`address(0)`) | +| Stavanger (L2) | Native POL (`address(0)`) | Sepolia (L1) | POL ERC20 (`0x6a7c...`) | + +Token mapping logic is configurable in `application/external_block_processor.go` via the `tokenMappings` map. + +## Configuration + +### Appchain Config (`config.yaml`) + +Configures the bridge appchain. + +```yaml +chain_id: 42 + +data_dir: "/data" + +emitter_port: ":9090" + +rpc_port: ":8080" + +log_level: 1 + +required_chains: + - 11155111 # Ethereum Sepolia + - 50591822 # Stavanger testnet +``` + +### Pelacli Config (`pelacli.yaml`) + +Configures pelacli for consensus, chain monitoring, and external transaction processing. + +```yaml +data_dir: "/data" + +consensus: + ask_period: 1s + +api: + port: 8081 + +appchains: + - chain_id: 42 + address: "appchain:9090" + +# Chains to monitor for bridge events +read_chains: + - chain_id: 50591822 # Stavanger testnet + api_key: "wss://stavanger-rpc.eu-north-2.gateway.fm/ws" + block_offset: 1 + +# Chains to send external transactions to +write_chains: + - chain_id: 11155111 # Ethereum Sepolia + rpc_url: "https://eth-sepolia.g.alchemy.com/v2/" + private_key: "" + pelagos_contract: "0x049FBea1295B569378Fe0D5AB965131743f332b9" + + - chain_id: 50591822 # Stavanger testnet + rpc_url: "https://stavanger-rpc.eu-north-2.gateway.fm" + private_key: "" + pelagos_contract: "0x416b560B03e6d9EF473bf57ccbb2A569AF5d0736" +``` + +⚠️ **Security:** Never commit real private keys. Use environment variables or secret management. + +## API Endpoints + +The bridge exposes a JSON-RPC endpoint for querying bridge state: + +### Bridge Methods +- `getBridgeStatus(bridgeId)` - Get bridge status and tx hashes + +Example: +```bash +curl -X POST http://localhost:8080/rpc \ + -H "Content-Type: application/json" \ + -d '{ + "jsonrpc": "2.0", + "method": "getBridgeStatus", + "params": [{"bridgeId": "0x123..."}], + "id": 1 + }' +``` + +## Development + +### Building + +```bash +# Build the appchain +make build + +# Run tests +make tests + +# Run with race detection +go test -race ./... + +# Lint code +make lints +``` + +### Adding New Chains + +1. Deploy Bridge.sol to the new chain +2. Add chain to `read_chains` in `pelacli.yaml` +3. Add chain to `write_chains` in `pelacli.yaml` +4. Add chain ID to `required_chains` in `config.yaml` +5. Update bridge contracts and token mappings in `external_block_processor.go` +6. Add subscription in `subscription.go` +7. Restart the bridge stack + +### Adding New Tokens + +1. Add token mapping in `application/external_block_processor.go` `tokenMappings` map +2. Update frontend token selector (if using frontend) +3. Restart the bridge appchain + +## Security Considerations + +### Current Implementation + +- βœ… **ReentrancyGuard** - Protects against reentrancy attacks +- βœ… **Pausable** - Emergency pause functionality +- βœ… **Access control** - Only Pelagos contract can call `executeTransaction()` +- βœ… **Balance checks** - Validates sufficient liquidity before claims +- βœ… **Event validation** - Verifies event signatures and contract addresses + +### Production Recommendations + +- πŸ”’ **Multi-sig validator** - Replace single-key validator with multi-sig +- πŸ”’ **Rate limiting** - Add per-address bridge limits +- πŸ”’ **Amount caps** - Set maximum bridge amounts +- πŸ”’ **Circuit breakers** - Automatic pause on anomalies +- πŸ”’ **Monitoring** - Set up alerts for large bridges +- πŸ”’ **Audit** - Get smart contracts audited before mainnet + +## Monitoring & Logs + +### Bridge Appchain Logs + +```bash +docker compose logs -f appchain +``` + +Key log messages: +- `Bridge event processed` - Event detected and processed +- `Processed EVM External block` - Block processing complete + +### Pelacli Logs + +```bash +docker compose logs -f pelacli +``` + +Key log messages: +- `Processing external block` - Monitoring external chains +- `Submitting external transaction` - Sending mint transaction + +## Troubleshooting + +### Bridge transaction not completing + +1. Check pelacli is running: `docker compose ps` +2. Check RPC connectivity: Verify RPC URLs in `pelacli.yaml` +3. Check logs: `docker compose logs pelacli | grep -i error` +4. Verify liquidity: Destination bridge must have sufficient tokens + +### Event not detected + +1. Verify event signature in `external_block_processor.go` matches contract +2. Check bridge contract address in `external_block_processor.go` matches deployed contract +3. Verify pelacli is monitoring the correct chain in `pelacli.yaml` +4. Check `start_block` in `pelacli.yaml` is before the bridge transaction + +### Frontend connection issues + +1. Verify MetaMask is installed +2. Check correct network is selected +3. Verify RPC URLs in frontend `app.js` +4. Check browser console for errors + +## License + +MIT License - see LICENSE file for details +**Built with [Pelagos SDK](https://github.com/0xAtelerix/sdk) 🌊** diff --git a/application/api/api.go b/application/api/api.go index 16d6e82..fb561ae 100644 --- a/application/api/api.go +++ b/application/api/api.go @@ -3,10 +3,10 @@ package api import ( "context" "encoding/json" + "errors" "fmt" "github.com/0xAtelerix/sdk/gosdk/rpc" - "github.com/holiman/uint256" "github.com/ledgerwatch/erigon-lib/kv" "github.com/0xAtelerix/example/application" @@ -15,89 +15,75 @@ import ( type CustomRPC struct { rpcServer *rpc.StandardRPCServer db kv.RoDB + cfg *application.AppConfig } -func NewCustomRPC(rpcServer *rpc.StandardRPCServer, db kv.RoDB) *CustomRPC { +func NewCustomRPC( + rpcServer *rpc.StandardRPCServer, + db kv.RoDB, + cfg *application.AppConfig, +) *CustomRPC { return &CustomRPC{ rpcServer: rpcServer, db: db, + cfg: cfg, } } func (c *CustomRPC) AddRPCMethods() { - c.rpcServer.AddMethod("getBalance", c.GetBalance) + c.rpcServer.AddMethod("getBridgeStatus", c.GetBridgeStatus) + c.rpcServer.AddMethod("getSupportedNetworks", c.GetSupportedNetworks) } -func (c *CustomRPC) GetBalance(ctx context.Context, params []any) (any, error) { +// GetSupportedNetworks returns the list of supported networks and their bridge contracts +func (c *CustomRPC) GetSupportedNetworks(_ context.Context, _ []any) (any, error) { + networks := make([]NetworkInfo, 0, len(c.cfg.Bridge.Contracts)) + + for chainID, contract := range c.cfg.Bridge.Contracts { + networks = append(networks, NetworkInfo{ + ChainID: chainID, + Contract: contract, + }) + } + + return GetSupportedNetworksResponse{Networks: networks}, nil +} + +// GetBridgeStatus retrieves the status of a bridge event +func (c *CustomRPC) GetBridgeStatus(ctx context.Context, params []any) (any, error) { if len(params) == 0 { return nil, application.ErrMissingParameters } - // Convert params[0] directly to balanceReq using json marshal/unmarshal paramBytes, err := json.Marshal(params[0]) if err != nil { return nil, fmt.Errorf("failed to marshal parameter: %w", err) } - var balanceReq GetBalanceRequest - if unmarshalErr := json.Unmarshal(paramBytes, &balanceReq); unmarshalErr != nil { - return nil, fmt.Errorf("invalid parameters: %w", unmarshalErr) - } - - // Get balance from database - balance, err := c.getBalance(ctx, balanceReq.User, balanceReq.Token) - if err != nil { - return nil, fmt.Errorf("failed to get balance: %w", err) - } - - response := GetBalanceResponse{ - User: balanceReq.User, - Token: balanceReq.Token, - Balance: balance.String(), - } - - return response, nil -} + var req GetBridgeStatusRequest -func (c *CustomRPC) getBalance( - ctx context.Context, - user, token string, -) (*uint256.Int, error) { - if c.db == nil { - return uint256.NewInt(0), application.ErrDatabaseNotAvailable + if unmarshalErr := json.Unmarshal(paramBytes, &req); unmarshalErr != nil { + return nil, fmt.Errorf("invalid parameters: %w", unmarshalErr) } - tx, err := c.db.BeginRo(ctx) - if err != nil { - return nil, fmt.Errorf("failed to begin transaction: %w", err) + if req.BridgeID == "" { + return nil, application.ErrInvalidBridgeID } - defer tx.Rollback() - - // Get balance from accounts bucket - accountKey := application.AccountKey(user, token) - balanceData, err := tx.GetOne(application.AccountsBucket, accountKey) + event, err := application.GetBridgeEvent(ctx, c.db, req.BridgeID) if err != nil { - return nil, fmt.Errorf("failed to get balance: %w", err) - } + if errors.Is(err, application.ErrBridgeNotFound) { + return nil, application.ErrBridgeNotFound + } - balance := uint256.NewInt(0) - if len(balanceData) > 0 { - balance.SetBytes(balanceData) + return nil, fmt.Errorf("failed to get bridge status: %w", err) } - return balance, nil -} - -// GetBalanceRequest represents a balance query request -type GetBalanceRequest struct { - User string `json:"user"` - Token string `json:"token"` -} - -// GetBalanceResponse represents a balance query response -type GetBalanceResponse struct { - User string `json:"user"` - Token string `json:"token"` - Balance string `json:"balance"` + return GetBridgeStatusResponse{ + BridgeID: req.BridgeID, + Status: event.Status, + Claimed: event.Status == application.BridgeStatusCompleted, + SourceTxHash: event.SourceTxHash, + ClaimTxHash: event.ClaimTxHash, + }, nil } diff --git a/application/api/api_test.go b/application/api/api_test.go index 14b7ead..28b404e 100644 --- a/application/api/api_test.go +++ b/application/api/api_test.go @@ -1,310 +1,189 @@ package api import ( - "bytes" "context" - "io" - "net/http" - "strings" - "sync" + "encoding/json" + "path/filepath" "testing" - "time" - "github.com/0xAtelerix/sdk/gosdk/rpc" - "github.com/0xAtelerix/sdk/gosdk/txpool" - "github.com/holiman/uint256" "github.com/ledgerwatch/erigon-lib/kv" "github.com/ledgerwatch/erigon-lib/kv/mdbx" - "github.com/ledgerwatch/erigon-lib/kv/memdb" mdbxlog "github.com/ledgerwatch/log/v3" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/0xAtelerix/example/application" ) -// createTempDBWithBalance creates a temporary in-memory database with test balance data -func createTempDBWithBalance(t *testing.T, user, token string, balance uint64) kv.RoDB { +func setupAPITestDB(t *testing.T) kv.RwDB { t.Helper() - db := memdb.New("") - ctx := context.Background() - - // Create tables - tx, err := db.BeginRw(ctx) - if err != nil { - t.Fatalf("Failed to begin transaction: %v", err) - } - - // Create the accounts bucket/table - if err := tx.CreateBucket(application.AccountsBucket); err != nil { - t.Fatalf("Failed to create accounts bucket: %v", err) - } - - accountKey := application.AccountKey(user, token) - - balanceValue := uint256.NewInt(balance) - if err := tx.Put(application.AccountsBucket, accountKey, balanceValue.Bytes()); err != nil { - t.Fatalf("Failed to set test balance: %v", err) - } + dbPath := filepath.Join(t.TempDir(), "test.db") + db, err := mdbx.NewMDBX(mdbxlog.New()). + Path(dbPath). + WithTableCfg(func(_ kv.TableCfg) kv.TableCfg { + return application.Tables() + }). + Open() + require.NoError(t, err) - if err := tx.Commit(); err != nil { - t.Fatalf("Failed to commit transaction: %v", err) - } + t.Cleanup(func() { + db.Close() + }) return db } -func TestCustomRPC_GetBalance(t *testing.T) { - // Create temp DB with balance - db := createTempDBWithBalance(t, "alice", "USDT", 1000) - defer db.Close() +func insertAPITestEvent(t *testing.T, db kv.RwDB, event application.BridgeEvent) { + t.Helper() - ctx := context.Background() + err := db.Update(context.Background(), func(tx kv.RwTx) error { + data, err := json.Marshal(event) + if err != nil { + return err + } - // Create RPC server and custom RPC - rpcServer := rpc.NewStandardRPCServer(nil) - customRPC := NewCustomRPC(rpcServer, db) - customRPC.AddRPCMethods() - - tests := []struct { - name string - params []any - expectedUser string - expectedToken string - expectedBalance string - expectError bool - }{ - { - name: "valid balance request", - params: []any{ - map[string]any{ - "user": "alice", - "token": "USDT", - }, - }, - expectedUser: "alice", - expectedToken: "USDT", - expectedBalance: "1000", - expectError: false, - }, - { - name: "zero balance for non-existent account", - params: []any{ - map[string]any{ - "user": "bob", - "token": "USDT", - }, - }, - expectedUser: "bob", - expectedToken: "USDT", - expectedBalance: "0", - expectError: false, - }, - { - name: "missing parameters", - params: []any{}, - expectError: true, - }, - { - name: "invalid parameters format", - params: []any{ - "invalid", - }, - expectError: true, + return tx.Put(application.BridgeEventsBucket, []byte(event.BridgeID), data) + }) + require.NoError(t, err) +} + +func testAppConfig() *application.AppConfig { + return &application.AppConfig{ + Bridge: application.BridgeConfig{ + Contracts: map[uint64]string{}, + TokenMappings: map[uint64]map[string]string{}, }, } +} - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result, err := customRPC.GetBalance(ctx, tt.params) - - if tt.expectError { - if err == nil { - t.Error("Expected error but got none") - } - - return - } - - if err != nil { - t.Errorf("Unexpected error: %v", err) - - return - } - - // Check result type - response, ok := result.(GetBalanceResponse) - if !ok { - t.Errorf("Expected GetBalanceResponse, got %T", result) - - return - } - - if response.User != tt.expectedUser { - t.Errorf("Expected user %s, got %s", tt.expectedUser, response.User) - } +func TestGetBridgeStatus_Found(t *testing.T) { + db := setupAPITestDB(t) + ctx := context.Background() - if response.Token != tt.expectedToken { - t.Errorf("Expected token %s, got %s", tt.expectedToken, response.Token) - } + rpc := NewCustomRPC(nil, db, testAppConfig()) - if response.Balance != tt.expectedBalance { - t.Errorf("Expected balance %s, got %s", tt.expectedBalance, response.Balance) - } - }) + event := application.BridgeEvent{ + BridgeID: "0x1234", + SourceChain: 11155111, + DestChain: 50591822, + Status: application.BridgeStatusConfirmed, + SourceTxHash: "0xsourcetx", } -} + insertAPITestEvent(t, db, event) -// Integration test: start RPC server, send transaction, get transaction by hash -func TestDefaultRPC_Integration_SendAndGetTransaction(t *testing.T) { - localDB, err := mdbx.NewMDBX(mdbxlog.New()). - Path(t.TempDir()). - WithTableCfg(func(_ kv.TableCfg) kv.TableCfg { - return txpool.Tables() - }). - Open() + params := []any{map[string]any{"bridgeId": "0x1234"}} + result, err := rpc.GetBridgeStatus(ctx, params) require.NoError(t, err) - defer localDB.Close() - - txPool := txpool.NewTxPool[application.Transaction[application.Receipt], application.Receipt]( - localDB, - ) + resp, ok := result.(GetBridgeStatusResponse) + require.True(t, ok) + assert.Equal(t, "0x1234", resp.BridgeID) + assert.Equal(t, application.BridgeStatusConfirmed, resp.Status) + assert.False(t, resp.Claimed) + assert.Equal(t, "0xsourcetx", resp.SourceTxHash) +} - // Create appchain DB for AddStandardMethods - appchainDB, err := mdbx.NewMDBX(mdbxlog.New()). - Path(t.TempDir()). - Open() - require.NoError(t, err) +func TestGetBridgeStatus_Completed(t *testing.T) { + db := setupAPITestDB(t) + ctx := context.Background() - defer appchainDB.Close() + rpc := NewCustomRPC(nil, db, testAppConfig()) - rpcServer := rpc.NewStandardRPCServer(nil) - rpc.AddStandardMethods[ - application.Transaction[application.Receipt], - application.Receipt, - application.Block, - ](rpcServer, appchainDB, txPool, 42) + event := application.BridgeEvent{ + BridgeID: "0x1234", + SourceChain: 11155111, + DestChain: 50591822, + Status: application.BridgeStatusCompleted, + SourceTxHash: "0xsourcetx", + ClaimTxHash: "0xclaimtx", + } + insertAPITestEvent(t, db, event) - rpcAddress := "http://127.0.0.1:18545/rpc" + params := []any{map[string]any{"bridgeId": "0x1234"}} + result, err := rpc.GetBridgeStatus(ctx, params) + require.NoError(t, err) - errServer := make(chan error, 1) - wg := sync.WaitGroup{} - wg.Add(1) + resp, ok := result.(GetBridgeStatusResponse) + require.True(t, ok) + assert.Equal(t, application.BridgeStatusCompleted, resp.Status) + assert.True(t, resp.Claimed) + assert.Equal(t, "0xclaimtx", resp.ClaimTxHash) +} - go func() { - wg.Done() +func TestGetBridgeStatus_NotFound(t *testing.T) { + db := setupAPITestDB(t) + ctx := context.Background() - errServer <- rpcServer.StartHTTPServer(t.Context(), ":18545") - }() + rpc := NewCustomRPC(nil, db, testAppConfig()) - select { - case serverErr := <-errServer: - if serverErr != nil { - t.Fatalf("Failed to start HTTP server: %v", serverErr) - } - default: - // continue - wg.Wait() - time.Sleep(100 * time.Millisecond) - } + params := []any{map[string]any{"bridgeId": "0xnonexistent"}} + _, err := rpc.GetBridgeStatus(ctx, params) + require.Error(t, err) + assert.Equal(t, application.ErrBridgeNotFound, err) +} - txHash := "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" +func TestGetBridgeStatus_MissingParams(t *testing.T) { + db := setupAPITestDB(t) + ctx := context.Background() - // Send transaction via JSON-RPC (include hash) - jsonReq := `{"jsonrpc":"2.0","method":"sendTransaction","params":[{"sender":"alice","token":"USDT","amount":"1234","hash":"` + txHash + `"}],"id":1}` - resp, err := sendJSONRPCRequest(rpcAddress, jsonReq) - require.NoError(t, err) - require.Contains(t, resp, "result") + rpc := NewCustomRPC(nil, db, testAppConfig()) - jsonReqGet := `{"jsonrpc":"2.0","method":"getTransaction","params":["` + txHash + `"],"id":2}` - respGet, err := sendJSONRPCRequest(rpcAddress, jsonReqGet) - require.NoError(t, err) - require.Contains(t, respGet, "result") + // Empty params + _, err := rpc.GetBridgeStatus(ctx, []any{}) + require.Error(t, err) + assert.Equal(t, application.ErrMissingParameters, err) - require.Contains(t, respGet, "alice") - require.Contains(t, respGet, "USDT") - require.Contains(t, respGet, "1234") + // Missing bridgeId + params := []any{map[string]any{}} + _, err = rpc.GetBridgeStatus(ctx, params) + require.Error(t, err) + assert.Equal(t, application.ErrInvalidBridgeID, err) } -func TestCustomRPC_GetBalance_NilDatabase(t *testing.T) { - // Test with nil database - rpcServer := rpc.NewStandardRPCServer(nil) - customRPC := NewCustomRPC(rpcServer, nil) +func TestGetSupportedNetworks(t *testing.T) { + db := setupAPITestDB(t) + ctx := context.Background() - params := []any{ - map[string]any{ - "user": "alice", - "token": "USDT", + cfg := &application.AppConfig{ + Bridge: application.BridgeConfig{ + Contracts: map[uint64]string{ + 11155111: "0x844E740Ea7F404c6208fd85Ee6114a14F8037df7", + 50591822: "0x3C1c8351a09DB0300786148B56EcB7be2FaA322e", + }, + TokenMappings: map[uint64]map[string]string{}, }, } - _, err := customRPC.GetBalance(context.Background(), params) - if err == nil || !strings.Contains(err.Error(), application.ErrDatabaseNotAvailable.Error()) { - t.Errorf( - "Expected error containing %q, got %v", - application.ErrDatabaseNotAvailable.Error(), - err, - ) - } -} + rpc := NewCustomRPC(nil, db, cfg) -func TestDefaultRPC_MethodRegistration(t *testing.T) { - // Create local DB for txpool - localDB, err := mdbx.NewMDBX(mdbxlog.New()). - Path(t.TempDir()). - WithTableCfg(func(_ kv.TableCfg) kv.TableCfg { - return txpool.Tables() - }). - Open() + result, err := rpc.GetSupportedNetworks(ctx, nil) require.NoError(t, err) - defer localDB.Close() - - // Create txpool - txPool := txpool.NewTxPool[application.Transaction[application.Receipt], application.Receipt]( - localDB, - ) + resp, ok := result.(GetSupportedNetworksResponse) + require.True(t, ok) + assert.Len(t, resp.Networks, 2) - // Create RPC server and add standard methods - rpcServer := rpc.NewStandardRPCServer(nil) - - // Test that AddStandardMethods doesn't panic (even with minimal setup) - require.NotPanics(t, func() { - rpc.AddStandardMethods[ - application.Transaction[application.Receipt], - application.Receipt, - application.Block, - ](rpcServer, nil, txPool, 42) - }) -} - -// Helper: send JSON-RPC request to local server -func sendJSONRPCRequest(rpcAddress string, jsonReq string) (string, error) { - req, err := http.NewRequestWithContext( - context.Background(), - http.MethodPost, - rpcAddress, - bytes.NewBufferString(jsonReq), - ) - if err != nil { - return "", err + // Check that both networks are present + chainIDs := make(map[uint64]string) + for _, n := range resp.Networks { + chainIDs[n.ChainID] = n.Contract } - req.Header.Set("Content-Type", "application/json") + assert.Equal(t, "0x844E740Ea7F404c6208fd85Ee6114a14F8037df7", chainIDs[11155111]) + assert.Equal(t, "0x3C1c8351a09DB0300786148B56EcB7be2FaA322e", chainIDs[50591822]) +} - client := &http.Client{} +func TestGetSupportedNetworks_Empty(t *testing.T) { + db := setupAPITestDB(t) + ctx := context.Background() - resp, err := client.Do(req) - if err != nil { - return "", err - } - defer resp.Body.Close() + rpc := NewCustomRPC(nil, db, testAppConfig()) - body, err := io.ReadAll(resp.Body) - if err != nil { - return "", err - } + result, err := rpc.GetSupportedNetworks(ctx, nil) + require.NoError(t, err) - return string(body), nil + resp, ok := result.(GetSupportedNetworksResponse) + require.True(t, ok) + assert.Empty(t, resp.Networks) } diff --git a/application/api/middleware.go b/application/api/middleware.go deleted file mode 100644 index d387098..0000000 --- a/application/api/middleware.go +++ /dev/null @@ -1,53 +0,0 @@ -package api - -import ( - "errors" - "net/http" - - "github.com/0xAtelerix/sdk/gosdk/rpc" - "github.com/rs/zerolog" -) - -// ErrNilRequestBody is returned when the request body is nil -var ErrNilRequestBody = errors.New("request body is nil") - -type ExampleMiddleware struct { - log zerolog.Logger -} - -func NewExampleMiddleware(log zerolog.Logger) *ExampleMiddleware { - return &ExampleMiddleware{ - log: log, - } -} - -func (e *ExampleMiddleware) ProcessRequest( - _ http.ResponseWriter, - r *http.Request, -) error { - e.log.Info().Msgf("Processing request: %s %s", r.Method, r.URL.Path) - - if r.Body == nil { // Dummy check for example - return ErrNilRequestBody - } - - return nil -} - -func (e *ExampleMiddleware) ProcessResponse( - _ http.ResponseWriter, - _ *http.Request, - response rpc.JSONRPCResponse, -) error { - e.log.Info().Msgf("Processing response ID: %v", response.ID) - - if response.Error != nil { // Dummy check for example - e.log.Error().Msgf("Error in response: %v", response.Error) - - return response.Error - } - - e.log.Info().Msgf("Response result: %v", response.Result) - - return nil -} diff --git a/application/api/types.go b/application/api/types.go new file mode 100644 index 0000000..5f62327 --- /dev/null +++ b/application/api/types.go @@ -0,0 +1,22 @@ +package api + +type GetBridgeStatusRequest struct { + BridgeID string `json:"bridgeId"` +} + +type GetBridgeStatusResponse struct { + BridgeID string `json:"bridgeId"` + Status string `json:"status"` + Claimed bool `json:"claimed"` + SourceTxHash string `json:"sourceTxHash,omitempty"` + ClaimTxHash string `json:"claimTxHash,omitempty"` +} + +type NetworkInfo struct { + ChainID uint64 `json:"chainId"` + Contract string `json:"contract"` +} + +type GetSupportedNetworksResponse struct { + Networks []NetworkInfo `json:"networks"` +} diff --git a/application/block.go b/application/block.go index 19b6175..1f514ed 100644 --- a/application/block.go +++ b/application/block.go @@ -10,11 +10,11 @@ import ( var _ apptypes.AppchainBlock = Block{} type Block struct { - BlockNum uint64 `json:"number"` - BlockHash [32]byte `json:"blockHash"` - ParentHash [32]byte `json:"parentHash"` - Root [32]byte `json:"root"` - Transactions []Transaction[Receipt] `json:"transactions,omitempty"` + BlockNum uint64 `json:"number"` + BlockHash [32]byte `json:"blockHash"` + ParentHash [32]byte `json:"parentHash"` + Root [32]byte `json:"root"` + Transactions []Transaction `json:"transactions,omitempty"` } func (b Block) Hash() [32]byte { @@ -26,10 +26,10 @@ func (b Block) StateRoot() [32]byte { } func BlockConstructor( - blockNumber uint64, // blockNumber - stateRoot [32]byte, // stateRoot - previousBlockHash [32]byte, // previousBlockHash - batch apptypes.Batch[Transaction[Receipt], Receipt], // txsBatch + blockNumber uint64, + stateRoot [32]byte, + previousBlockHash [32]byte, + batch apptypes.Batch[Transaction, Receipt], ) *Block { hasher := sha256.New() @@ -39,7 +39,6 @@ func BlockConstructor( hasher.Write(stateRoot[:]) hasher.Write(previousBlockHash[:]) - // Compute final hash var blockHash [32]byte copy(blockHash[:], hasher.Sum(nil)) diff --git a/application/bridge_event.go b/application/bridge_event.go new file mode 100644 index 0000000..41b52c4 --- /dev/null +++ b/application/bridge_event.go @@ -0,0 +1,21 @@ +package application + +// BridgeEvent represents a cross-chain bridge event from external chains. +type BridgeEvent struct { + BridgeID string `json:"bridgeId"` // Unique bridge identifier + SourceChain uint64 `json:"sourceChain"` // Source chain ID + DestChain uint64 `json:"destChain"` // Destination chain ID + Token string `json:"token"` // Token address (address(0) for native) + Amount string `json:"amount"` // Amount as string (supports > uint64) + Sender string `json:"sender"` // Original sender address + Recipient string `json:"recipient"` // Recipient on destination chain + Status string `json:"status"` // Confirmed or Completed + SourceTxHash string `json:"sourceTxHash,omitempty"` // Source chain tx hash + ClaimTxHash string `json:"claimTxHash,omitempty"` // Destination chain tx hash +} + +// BridgeEvent status constants +const ( + BridgeStatusConfirmed = "Confirmed" // Event received and external tx generated + BridgeStatusCompleted = "Completed" // Claimed on destination chain +) diff --git a/application/buckets.go b/application/buckets.go index 18a439b..2715294 100644 --- a/application/buckets.go +++ b/application/buckets.go @@ -3,11 +3,11 @@ package application import "github.com/ledgerwatch/erigon-lib/kv" const ( - AccountsBucket = "appaccounts" // token+account -> value + BridgeEventsBucket = "bridge_events" // bridgeId -> BridgeEvent ) func Tables() kv.TableCfg { return kv.TableCfg{ - AccountsBucket: {}, + BridgeEventsBucket: {}, } } diff --git a/application/config.go b/application/config.go new file mode 100644 index 0000000..bf3bbf0 --- /dev/null +++ b/application/config.go @@ -0,0 +1,71 @@ +package application + +import ( + "fmt" + "os" + + "github.com/0xAtelerix/sdk/gosdk" + "github.com/ethereum/go-ethereum/common" + "gopkg.in/yaml.v3" +) + +// BridgeConfig holds configuration for bridge contracts and token mappings. +type BridgeConfig struct { + // Contracts maps chainID -> bridge contract address + Contracts map[uint64]string `yaml:"contracts"` + // TokenMappings maps sourceChainID -> sourceToken -> destToken + TokenMappings map[uint64]map[string]string `yaml:"token_mappings"` +} + +// AppConfig embeds the SDK config and adds bridge-specific configuration. +type AppConfig struct { + gosdk.InitConfig `yaml:",inline"` + Bridge BridgeConfig `yaml:"bridge"` + MetricsPort int `yaml:"metrics_port"` +} + +// LoadConfig loads both SDK and bridge configuration from a YAML file. +// If path is empty, returns default configuration. +func LoadConfig(path string) (*AppConfig, error) { + if path == "" { + return &AppConfig{}, nil + } + + data, err := os.ReadFile(path) + if err != nil { + return nil, fmt.Errorf("read config file: %w", err) + } + + var cfg AppConfig + if err := yaml.Unmarshal(data, &cfg); err != nil { + return nil, fmt.Errorf("parse config: %w", err) + } + + return &cfg, nil +} + +// GetBridgeContracts returns the bridge contracts map with parsed addresses. +func (c *BridgeConfig) GetBridgeContracts() map[uint64]common.Address { + contracts := make(map[uint64]common.Address) + + for chainID, addr := range c.Contracts { + contracts[chainID] = common.HexToAddress(addr) + } + + return contracts +} + +// GetTokenMappings returns the token mappings with parsed addresses. +func (c *BridgeConfig) GetTokenMappings() map[uint64]map[common.Address]common.Address { + mappings := make(map[uint64]map[common.Address]common.Address) + + for chainID, tokens := range c.TokenMappings { + mappings[chainID] = make(map[common.Address]common.Address) + + for src, dst := range tokens { + mappings[chainID][common.HexToAddress(src)] = common.HexToAddress(dst) + } + } + + return mappings +} diff --git a/application/errors.go b/application/errors.go index c257cc3..46a0dd2 100644 --- a/application/errors.go +++ b/application/errors.go @@ -7,8 +7,9 @@ func (e Error) Error() string { } const ( - ErrNotEnoughBalance = Error("sender's balance not enough") - ErrDatabaseNil = Error("database is nil") - ErrMissingParameters = Error("missing parameters") - ErrDatabaseNotAvailable = Error("database not available") + ErrMissingParameters = Error("missing parameters") + + // Bridge errors + ErrBridgeNotFound = Error("bridge not found") + ErrInvalidBridgeID = Error("invalid bridge ID") ) diff --git a/application/external_block_processor.go b/application/external_block_processor.go index 437cfa3..57479c8 100644 --- a/application/external_block_processor.go +++ b/application/external_block_processor.go @@ -2,110 +2,77 @@ package application import ( "context" + "encoding/json" "math/big" + "strconv" "strings" "github.com/0xAtelerix/sdk/gosdk" "github.com/0xAtelerix/sdk/gosdk/apptypes" "github.com/0xAtelerix/sdk/gosdk/evmtypes" "github.com/0xAtelerix/sdk/gosdk/external" - "github.com/0xAtelerix/sdk/gosdk/library" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" - "github.com/holiman/uint256" "github.com/ledgerwatch/erigon-lib/kv" "github.com/rs/zerolog/log" + + "github.com/0xAtelerix/example/application/metrics" ) const ( - // ExampleContractAddress is the deployed Example contract address - // - // DEPLOYMENT INSTRUCTIONS: - // 1. Navigate to the SDK contracts directory: - // cd /path/to/0xAtelerix/sdk/contracts - // 2. Deploy the Example contract: - // cd scripts && ./deploy_example.sh - // 3. Update this address with your deployed contract address - // 4. Update signature or ABI if your contract events differ - // - // This is a demo address on Polygon-Amoy testnet. - ExampleContractAddress = "0x8D350d5351A936Ef3e2907C0a438Fc941DAE3bfd" - - // Event signatures for the Example contract events - // These correspond to events in 0xAtelerix/sdk/contracts/example/Example.sol - // Deposit(address,string,uint256) event signature - DepositEventSignature = "0x2d4b597935f3cd67fb2eebf1db4debc934cee5c7baa7153f980fdbeb2e74084e" - // Swap(address,string,string,uint256) event signature - SwapEventSignature = "0x363ba239c72b81c4726aba8829ad4df22628bf7d09efc5f7a18063a53ec1c4ba" - // WithdrawToSolana(uint256) event signature - // This event triggers a cross-chain transfer from EVM to Solana - WithdrawToSolanaSignature = "0x245ecbfbddf346446b302f2dc8237ed1144f6f9407cb9708e2d0734458c72950" - - // ABI definitions for event decoding - depositEventABI = `[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address",` + - `"name":"user","type":"address"},{"indexed":false,"internalType":"string","name":"token",` + - `"type":"string"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],` + - `"name":"Deposit","type":"event"}]` - - swapEventABI = `[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address",` + - `"name":"user","type":"address"},{"indexed":false,"internalType":"string","name":"tokenIn",` + - `"type":"string"},{"indexed":false,"internalType":"string","name":"tokenOut","type":"string"},` + - `{"indexed":false,"internalType":"uint256","name":"amountIn","type":"uint256"}],"name":"Swap","type":"event"}]` - - withdrawToSolanaABI = `[{"anonymous":false,"inputs":[` + - `{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],` + - `"name":"WithdrawToSolana","type":"event"}]` + BridgeInitiatedSigHash = "0xa43a2e0bb4454dc2f20f4a34be7549f0e1b00e4f5e88805c729900e40471a0cb" + AssetClaimedSigHash = "0x260120404c049bd806f3d5d3444295a9eab7f94112fdec90a6072ad39acae708" ) -// Verify ExtBlockProcessor implements ExternalBlockProcessor interface. +const bridgeInitiatedEventABI = `[{"anonymous":false,"inputs":[` + + `{"indexed":true,"internalType":"bytes32","name":"bridgeId","type":"bytes32"},` + + `{"indexed":true,"internalType":"uint256","name":"sourceChainId","type":"uint256"},` + + `{"indexed":true,"internalType":"uint256","name":"destChainId","type":"uint256"},` + + `{"indexed":false,"internalType":"address","name":"token","type":"address"},` + + `{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},` + + `{"indexed":false,"internalType":"address","name":"sender","type":"address"},` + + `{"indexed":false,"internalType":"address","name":"recipient","type":"address"}],` + + `"name":"BridgeInitiated","type":"event"}]` + var _ gosdk.ExternalBlockProcessor = &ExtBlockProcessor{} type ExtBlockProcessor struct { - msa gosdk.MultichainStateAccessor + msa gosdk.MultichainStateAccessor + bridgeABI abi.ABI + bridgeContracts map[uint64]common.Address // chainID -> bridge contract + tokenMappings map[uint64]map[common.Address]common.Address // sourceChain -> token -> destToken } -func NewExtBlockProcessor(msa gosdk.MultichainStateAccessor) gosdk.ExternalBlockProcessor { +func NewExtBlockProcessor( + msa gosdk.MultichainStateAccessor, + cfg *AppConfig, +) *ExtBlockProcessor { + bridgeABI, err := abi.JSON(strings.NewReader(bridgeInitiatedEventABI)) + if err != nil { + panic("failed to parse BridgeInitiated ABI: " + err.Error()) + } + return &ExtBlockProcessor{ - msa: msa, + msa: msa, + bridgeABI: bridgeABI, + bridgeContracts: cfg.Bridge.GetBridgeContracts(), + tokenMappings: cfg.Bridge.GetTokenMappings(), } } -// ProcessBlock handles external chain blocks (EVM, Solana). +// ProcessBlock processes external chain blocks (EVM chains only) func (p *ExtBlockProcessor) ProcessBlock( b apptypes.ExternalBlock, tx kv.RwTx, ) ([]apptypes.ExternalTransaction, error) { - switch { - case library.IsEvmChain(apptypes.ChainType(b.ChainID)): - return p.processEVMBlock(b, tx) - case library.IsSolanaChain(apptypes.ChainType(b.ChainID)): - return p.processSolanaBlock(b, tx) - default: - log.Warn().Uint64("chainID", b.ChainID).Msg("Unsupported external chain, skipping...") - } + if _, ok := p.bridgeContracts[b.ChainID]; !ok { + log.Warn().Uint64("chainID", b.ChainID).Msg("Unsupported chain, skipping...") - return nil, nil -} - -func (p *ExtBlockProcessor) processSolanaBlock( - b apptypes.ExternalBlock, - _ kv.RwTx, -) ([]apptypes.ExternalTransaction, error) { - var externalTxs []apptypes.ExternalTransaction - - solBlock, err := p.msa.SolanaBlock(context.Background(), b) - if err != nil { - return nil, err + return nil, nil } - log.Info(). - Uint64("chainID", b.ChainID). - Uint64("slotNumber", b.BlockNumber). - Int("transactions", len(solBlock.Transactions)). - Msg("Solana External block") - - return externalTxs, nil + return p.processEVMBlock(b, tx) } func (p *ExtBlockProcessor) processEVMBlock( @@ -114,11 +81,6 @@ func (p *ExtBlockProcessor) processEVMBlock( ) ([]apptypes.ExternalTransaction, error) { var externalTxs []apptypes.ExternalTransaction - block, err := p.msa.EVMBlock(context.Background(), b) - if err != nil { - return nil, err - } - receipts, err := p.msa.EVMReceipts(context.Background(), b) if err != nil { return nil, err @@ -131,274 +93,307 @@ func (p *ExtBlockProcessor) processEVMBlock( } } + // Update last processed block metric + metrics.LastProcessedBlock.WithLabelValues(strconv.FormatUint(b.ChainID, 10)).Set(float64(b.BlockNumber)) + log.Info(). Uint64("chainID", b.ChainID). Uint64("blockNumber", b.BlockNumber). - Int("transactions", len(block.Body.Transactions)). Int("receipts", len(receipts)). - Msg("EVM External block") + Int("extTxs", len(externalTxs)). + Msg("Processed EVM external block") return externalTxs, nil } -// processReceipt handles Deposit events from the external chain -// Just for example, In real use-case, handle according to your logic -func (*ExtBlockProcessor) processReceipt( - tx kv.RwTx, +// processReceipt handles Bridge events from the external chain +func (p *ExtBlockProcessor) processReceipt( + dbtx kv.RwTx, r evmtypes.Receipt, chainID uint64, ) []apptypes.ExternalTransaction { var externalTxs []apptypes.ExternalTransaction for _, vlog := range r.Logs { - // Check if this log is from our example contract - if vlog.Address == common.HexToAddress(ExampleContractAddress) && len(vlog.Topics) > 0 { - switch vlog.Topics[0].Hex() { - case DepositEventSignature: - // Decode deposit event using ABI - token, amount, err := decodeDepositEvent(vlog) - if err != nil { - log.Error().Err(err).Msg("Failed to decode deposit event") - - continue - } - - // Extract user address from topics[1] (indexed parameter) - userAddr := common.HexToAddress(vlog.Topics[1].Hex()) - user := userAddr.Hex() - - // Convert to uint256 for storage - amountUint256, overflow := uint256.FromBig(amount) - if overflow { - log.Error().Str("amount", amount.String()).Msg("Deposit amount too large") - - continue - } - - // Update user balance in appchain - accountKey := AccountKey(user, token) - - // Get current balance - currentBalanceData, err := tx.GetOne(AccountsBucket, accountKey) - if err != nil { - log.Error().Err(err).Msg("Failed to get current balance") - - continue - } - - currentBalance := uint256.NewInt(0) - if len(currentBalanceData) > 0 { - currentBalance.SetBytes(currentBalanceData) - } - - // Add deposited amount - newBalance := uint256.NewInt(0).Add(currentBalance, amountUint256) - - // Store new balance - balanceBytes := newBalance.Bytes() - if err := tx.Put(AccountsBucket, accountKey, balanceBytes); err != nil { - log.Error().Err(err).Msg("Failed to update balance") - - continue - } - - log.Info(). - Uint64("chainID", chainID). - Str("user", userAddr.Hex()). - Str("token", token). - Str("amount", amount.String()). - Str("new_balance", newBalance.String()). - Msg("Processed deposit from external chain") - - case SwapEventSignature: - // Decode swap event using ABI - tokenIn, tokenOut, amountIn, err := decodeSwapEvent(vlog) - if err != nil { - log.Error().Err(err).Msg("Failed to decode swap event") - - continue - } - - userAddr := common.HexToAddress(vlog.Topics[1].Hex()) - - // Calculate output amount using fixed exchange rate - amountOut := calculateSwapOutput(tokenIn, tokenOut, amountIn) - - // Create an external transaction record for the destination chain (EVM) - extTx, err := external.NewExTxBuilder( - createTokenMintPayload(userAddr, amountOut, tokenOut), - library.EthereumSepoliaChainID). - Build() - if err != nil { - log.Error().Err(err).Msg("Failed to create external transaction for swap event") - - continue - } - - externalTxs = append(externalTxs, extTx) - - log.Info(). - Uint64("source_chainID", chainID). - Str("user", userAddr.Hex()). - Str("tokenIn", tokenIn). - Str("tokenOut", tokenOut). - Str("amountIn", amountIn.String()). - Str("amountOut", amountOut.String()). - Uint64("target_chainID", uint64(library.EthereumSepoliaChainID)). - Msg("Processed swap event - EVM to EVM") - - case WithdrawToSolanaSignature: - // Decode withdraw to Solana event using ABI - amount, err := decodeWithdrawToSolanaEvent(vlog) - if err != nil { - log.Error().Err(err).Msg("Failed to decode WithdrawToSolana event") - - continue - } - - // Create Solana mint payload for cross-chain transfer - extTx, err := createSolanaMintPayload(amount.Uint64()) - if err != nil { - log.Error().Err(err).Msg("Failed to create Solana mint payload") - - continue - } - - externalTxs = append(externalTxs, extTx) - - log.Info(). - Uint64("source_chainID", chainID). - Str("amount", amount.String()). - Uint64("target_chainID", uint64(library.SolanaDevnetChainID)). - Msg("Processed withdraw to Solana event - EVM to Solana withdraw") - - default: - log.Info().Msgf("Unhandled event signature: %s", vlog.Topics[0].Hex()) - } + // Check if log is from the expected bridge contract for this chain + if p.bridgeContracts[chainID] != vlog.Address || len(vlog.Topics) == 0 { + continue } - } - return externalTxs -} + switch vlog.Topics[0].Hex() { + case BridgeInitiatedSigHash: + bridgeEvent, err := p.decodeBridgeInitiatedEvent(vlog) + if err != nil { + log.Error().Err(err).Msg("Failed to decode BridgeInitiated event") -// calculateSwapOutput calculates the output amount for a token swap using fixed exchange rates -func calculateSwapOutput(tokenIn, tokenOut string, amountIn *big.Int) *big.Int { - // Fixed exchange rates for token pairs (tokenIn:tokenOut -> rate) - // Rate represents how many tokenOut you get for 1 tokenIn - exchangeRates := map[string]float64{ - "ETH:USDT": 4200.0, - "USDT:ETH": 1.0 / 4200.0, - "BTC:USDT": 60000.0, - "USDT:BTC": 1.0 / 60000.0, - } + continue + } - pair := tokenIn + ":" + tokenOut + // Validate source chain matches + if bridgeEvent.SourceChain != chainID { + log.Warn(). + Str("bridgeId", bridgeEvent.BridgeID). + Msg("Source chain mismatch, skipping") - rate, exists := exchangeRates[pair] - if !exists { - log.Warn().Str("pair", pair).Msg("Exchange rate not found, using 1:1 rate") + continue + } - return amountIn // Default to 1:1 if rate not found - } + // Validate destination chain is supported + if _, ok := p.bridgeContracts[bridgeEvent.DestChain]; !ok { + log.Warn(). + Str("bridgeId", bridgeEvent.BridgeID). + Uint64("destChain", bridgeEvent.DestChain). + Msg("Unsupported destination chain") + + continue + } + + // Skip if already processed + exists, err := dbtx.GetOne(BridgeEventsBucket, []byte(bridgeEvent.BridgeID)) + if err != nil { + log.Error().Err(err).Msg("Failed to check existing bridge event") + + continue + } + + if len(exists) > 0 { + continue + } + + // Create ExtTx for the destination chain + extTx, err := p.createMintTransaction( + bridgeEvent.DestChain, + bridgeEvent.BridgeID, + bridgeEvent.SourceChain, + bridgeEvent.Token, + bridgeEvent.Amount, + bridgeEvent.Recipient, + ) + if err != nil { + log.Error().Err(err).Str("bridgeId", bridgeEvent.BridgeID). + Msg("Failed to create mint transaction") + + continue + } - // Convert amountIn to float64 for calculation - amountInFloat := new(big.Float).SetInt(amountIn) - rateFloat := new(big.Float).SetFloat64(rate) + // Store event as Confirmed + if storeErr := storeBridgeEvent(dbtx, bridgeEvent); storeErr != nil { + log.Error(). + Err(storeErr). + Str("bridgeId", bridgeEvent.BridgeID). + Msg("Failed to store event") - // Calculate output amount - outputFloat := new(big.Float).Mul(amountInFloat, rateFloat) + continue // Don't return ExtTx if store fails + } + + // Update metrics + srcChain := strconv.FormatUint(bridgeEvent.SourceChain, 10) + dstChain := strconv.FormatUint(bridgeEvent.DestChain, 10) + metrics.BridgeTransactionsTotal.WithLabelValues(srcChain, dstChain, "confirmed").Inc() + metrics.BridgeTransactionsPending.Inc() + metrics.TrackPending(bridgeEvent.BridgeID) + + log.Info(). + Str("bridgeId", bridgeEvent.BridgeID). + Uint64("src", bridgeEvent.SourceChain). + Uint64("dst", bridgeEvent.DestChain). + Str("amount", bridgeEvent.Amount). + Msg("Bridge event processed") + + externalTxs = append(externalTxs, extTx) + + case AssetClaimedSigHash: + if len(vlog.Topics) < 2 { + continue + } + + bridgeID := vlog.Topics[1].Hex() + bridgeKey := []byte(bridgeID) + + existing, getErr := dbtx.GetOne(BridgeEventsBucket, bridgeKey) + if getErr != nil || len(existing) == 0 { + continue + } + + var event BridgeEvent + if unmarshalErr := json.Unmarshal(existing, &event); unmarshalErr != nil { + continue + } + + if event.Status == BridgeStatusCompleted { + continue + } + + // Update status and save + event.Status = BridgeStatusCompleted + event.ClaimTxHash = vlog.TxHash.Hex() - // Convert back to big.Int (round down) - outputInt := new(big.Int) - outputFloat.Int(outputInt) + updatedData, err := json.Marshal(event) + if err != nil { + log.Error().Err(err).Str("bridgeId", bridgeID).Msg("Failed to marshal bridge event") + + continue + } + + if err := dbtx.Put(BridgeEventsBucket, bridgeKey, updatedData); err != nil { + log.Error().Err(err).Str("bridgeId", bridgeID).Msg("Failed to update bridge event") + + continue + } + + // Update metrics + metrics.BridgeTransactionsPending.Dec() + metrics.ResolvePending(bridgeID) + metrics.BridgeTransactionsCompleted.Inc() + srcChain := strconv.FormatUint(event.SourceChain, 10) + dstChain := strconv.FormatUint(event.DestChain, 10) + metrics.BridgeTransactionsTotal.WithLabelValues(srcChain, dstChain, "completed").Inc() + + log.Info().Str("bridgeId", bridgeID).Msg("Bridge marked as completed") + + default: + // Ignore other events + } + } - return outputInt + return externalTxs } -// createTokenMintPayload creates a payload for the AppChain contract -// This matches the demo contracts in 0xAtelerix/sdk/contracts/pelacli/AppChain.sol -// Payload format: [recipient:20bytes][amount:32bytes][tokenName:variable] -// The AppChain contract will mint these tokens to the recipient address -func createTokenMintPayload(recipient common.Address, amount *big.Int, token string) []byte { - payload := make([]byte, 20+32+len(token)) - copy(payload[0:20], recipient.Bytes()) - amountBytes := amount.Bytes() - copy(payload[52-len(amountBytes):52], amountBytes) - copy(payload[52:], []byte(token)) - - return payload +// BridgeInitiatedEvent represents the decoded BridgeInitiated event +type BridgeInitiatedEvent struct { + BridgeID string + SourceChain uint64 + DestChain uint64 + Token string + Amount string // String to support amounts > uint64 + Sender string + Recipient string + TxHash string } -// decodeDepositEvent decodes a Deposit event using ABI -func decodeDepositEvent(vlog *types.Log) (string, *big.Int, error) { - // Parse the ABI - parsedABI, err := abi.JSON(strings.NewReader(depositEventABI)) - if err != nil { - return "", nil, err +// decodeBridgeInitiatedEvent decodes a BridgeInitiated event +func (p *ExtBlockProcessor) decodeBridgeInitiatedEvent( + vlog *types.Log, +) (*BridgeInitiatedEvent, error) { + if len(vlog.Topics) < 4 { + return nil, Error("insufficient topics in BridgeInitiated event") } - // Unpack the event data (non-indexed parameters) - var depositEvent struct { - Token string - Amount *big.Int + var eventData struct { + Token common.Address + Amount *big.Int + Sender common.Address + Recipient common.Address } - err = parsedABI.UnpackIntoInterface(&depositEvent, "Deposit", vlog.Data) - if err != nil { - return "", nil, err + if err := p.bridgeABI.UnpackIntoInterface(&eventData, "BridgeInitiated", vlog.Data); err != nil { + return nil, err } - return depositEvent.Token, depositEvent.Amount, nil + return &BridgeInitiatedEvent{ + BridgeID: vlog.Topics[1].Hex(), + SourceChain: vlog.Topics[2].Big().Uint64(), + DestChain: vlog.Topics[3].Big().Uint64(), + Token: eventData.Token.Hex(), + Amount: eventData.Amount.String(), + Sender: eventData.Sender.Hex(), + Recipient: eventData.Recipient.Hex(), + TxHash: vlog.TxHash.Hex(), + }, nil } -// decodeSwapEvent decodes a Swap event using ABI -func decodeSwapEvent(vlog *types.Log) (tokenIn, tokenOut string, amountIn *big.Int, err error) { - // Parse the ABI - parsedABI, err := abi.JSON(strings.NewReader(swapEventABI)) +// createMintTransaction generates an ExternalTransaction for minting tokens on the destination chain +func (p *ExtBlockProcessor) createMintTransaction( + destChainID uint64, + bridgeID string, + sourceChainID uint64, + tokenAddress string, + amount string, + recipientAddress string, +) (apptypes.ExternalTransaction, error) { + payload, err := p.createBridgePayload( + bridgeID, + sourceChainID, + tokenAddress, + amount, + recipientAddress, + ) if err != nil { - return "", "", nil, err + return apptypes.ExternalTransaction{}, err } - // Unpack the event data (non-indexed parameters) - var swapEvent struct { - TokenIn string - TokenOut string - AmountIn *big.Int - } - - err = parsedABI.UnpackIntoInterface(&swapEvent, "Swap", vlog.Data) + extTx, err := external.NewExTxBuilder(payload, apptypes.ChainType(destChainID)).Build() if err != nil { - return "", "", nil, err - } + log.Error(). + Err(err). + Uint64("destChain", destChainID). + Msg("Failed to build external transaction") - tokenIn = swapEvent.TokenIn - tokenOut = swapEvent.TokenOut - amountIn = swapEvent.AmountIn + return apptypes.ExternalTransaction{}, err + } - return + return extTx, nil } -// decodeWithdrawToSolanaEvent decodes a WithdrawToSolana event using ABI -func decodeWithdrawToSolanaEvent( - vlog *types.Log, -) (amount *big.Int, err error) { - // Parse the ABI - parsedABI, err := abi.JSON(strings.NewReader(withdrawToSolanaABI)) - if err != nil { - return nil, err +// createBridgePayload creates the 160-byte payload for Bridge.executeTransaction() +// Layout: bridgeId(32) | sourceChainId(32) | token(32, left-aligned) | amount(32) | recipient(32, left-aligned) +func (p *ExtBlockProcessor) createBridgePayload( + bridgeID string, + sourceChainID uint64, + tokenAddress string, + amount string, + recipientAddress string, +) ([]byte, error) { + payload := make([]byte, 160) + + // bridgeId (bytes32) + bridgeHash := common.HexToHash(bridgeID) + copy(payload[0:32], bridgeHash[:]) + + // sourceChainId (uint256) + copy(payload[32:64], new(big.Int).SetUint64(sourceChainID).FillBytes(make([]byte, 32))) + + // token (address, LEFT-aligned) - map if needed + token := common.HexToAddress(tokenAddress) + if mapped, ok := p.tokenMappings[sourceChainID][token]; ok { + token = mapped } - // Unpack the event data (non-indexed parameters) - var withdrawEvent struct { - Amount *big.Int + copy(payload[64:84], token[:]) + + // amount (uint256) - parse from string to support large values + amountBig, ok := new(big.Int).SetString(amount, 10) + if !ok { + return nil, Error("invalid amount: " + amount) } - err = parsedABI.UnpackIntoInterface(&withdrawEvent, "WithdrawToSolana", vlog.Data) - if err != nil { - return nil, err + copy(payload[96:128], amountBig.FillBytes(make([]byte, 32))) + + // recipient (address, LEFT-aligned) + recipient := common.HexToAddress(recipientAddress) + copy(payload[128:148], recipient[:]) + + return payload, nil +} + +// storeBridgeEvent stores a bridge event in the database +func storeBridgeEvent(dbtx kv.RwTx, bridgeEvent *BridgeInitiatedEvent) error { + event := BridgeEvent{ + BridgeID: bridgeEvent.BridgeID, + SourceChain: bridgeEvent.SourceChain, + DestChain: bridgeEvent.DestChain, + Token: bridgeEvent.Token, + Amount: bridgeEvent.Amount, + Sender: bridgeEvent.Sender, + Recipient: bridgeEvent.Recipient, + Status: BridgeStatusConfirmed, + SourceTxHash: bridgeEvent.TxHash, } - amount = withdrawEvent.Amount + eventData, err := json.Marshal(event) + if err != nil { + return err + } - return + return dbtx.Put(BridgeEventsBucket, []byte(bridgeEvent.BridgeID), eventData) } diff --git a/application/genesis.go b/application/genesis.go deleted file mode 100644 index 22316cc..0000000 --- a/application/genesis.go +++ /dev/null @@ -1,108 +0,0 @@ -package application - -import ( - "context" - "fmt" - - "github.com/holiman/uint256" - "github.com/ledgerwatch/erigon-lib/kv" - "github.com/rs/zerolog/log" -) - -// Optional -// GenesisAccount represents an initial account balance -type GenesisAccount struct { - Address string - Token string - Balance uint64 -} - -// GetDefaultGenesisAccounts returns the default genesis accounts with initial balances -func GetDefaultGenesisAccounts() []GenesisAccount { - return []GenesisAccount{ - // Alice - Well funded - {Address: "alice", Token: "USDT", Balance: 1000000000000}, // 1,000,000 USDT (6 decimals) - {Address: "alice", Token: "BTC", Balance: 1000000000}, // 10 BTC (8 decimals) - {Address: "alice", Token: "ETH", Balance: 10000000000}, // 100 ETH (8 decimals) - - // Bob - Well funded - {Address: "bob", Token: "USDT", Balance: 1000000000000}, // 1,000,000 USDT - {Address: "bob", Token: "BTC", Balance: 1000000000}, // 10 BTC - {Address: "bob", Token: "ETH", Balance: 10000000000}, // 100 ETH - - // Charlie - Medium funded - {Address: "charlie", Token: "USDT", Balance: 500000000000}, // 500,000 USDT - {Address: "charlie", Token: "BTC", Balance: 500000000}, // 5 BTC - {Address: "charlie", Token: "ETH", Balance: 5000000000}, // 50 ETH - - // Dave - Medium funded - {Address: "dave", Token: "USDT", Balance: 500000000000}, // 500,000 USDT - {Address: "dave", Token: "BTC", Balance: 500000000}, // 5 BTC - {Address: "dave", Token: "ETH", Balance: 5000000000}, // 50 ETH - - // Eve - Small funded - {Address: "eve", Token: "USDT", Balance: 100000000000}, // 100,000 USDT - {Address: "eve", Token: "BTC", Balance: 100000000}, // 1 BTC - {Address: "eve", Token: "ETH", Balance: 1000000000}, // 10 ETH - } -} - -// InitializeGenesis sets up initial account balances -func InitializeGenesis(ctx context.Context, db kv.RwDB) error { - if db == nil { - return ErrDatabaseNil - } - - // Begin transaction - tx, err := db.BeginRw(ctx) - if err != nil { - return fmt.Errorf("failed to begin transaction: %w", err) - } - - defer tx.Rollback() - - // Check if genesis has already been initialized by looking for genesis marker - genesisMarker := []byte("genesis_initialized") - - existing, err := tx.GetOne(AccountsBucket, genesisMarker) - if err != nil { - return fmt.Errorf("failed to check genesis marker: %w", err) - } - - if len(existing) > 0 { - log.Info().Msg("Genesis already initialized, skipping...") - - return tx.Commit() - } - - log.Info().Msg("First startup detected - initializing genesis state...") - - // Initialize accounts with genesis balances - for _, account := range GetDefaultGenesisAccounts() { - // Set account balance - accountKey := AccountKey(account.Address, account.Token) - balance := uint256.NewInt(account.Balance) - - err = tx.Put(AccountsBucket, accountKey, balance.Bytes()) - if err != nil { - return fmt.Errorf("failed to set genesis balance for %s:%s: %w", - account.Address, account.Token, err) - } - - log.Info(). - Str("address", account.Address). - Str("token", account.Token). - Uint64("balance", account.Balance). - Msg("Initialized account balance") - } - - // Set genesis marker to indicate initialization is complete - err = tx.Put(AccountsBucket, genesisMarker, []byte("true")) - if err != nil { - return fmt.Errorf("failed to set genesis marker: %w", err) - } - - log.Info().Msg("Genesis initialization completed successfully!") - - return tx.Commit() -} diff --git a/application/metrics/metrics.go b/application/metrics/metrics.go new file mode 100644 index 0000000..139ec3e --- /dev/null +++ b/application/metrics/metrics.go @@ -0,0 +1,138 @@ +package metrics + +import ( + "context" + "fmt" + "net/http" + "sync" + "time" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" + "github.com/rs/zerolog/log" +) + +var ( + // BridgeTransactionsTotal counts bridge transactions by chain and status + BridgeTransactionsTotal = prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: "bridge_transactions_total", + Help: "Total number of bridge transactions", + }, + []string{"source_chain", "dest_chain", "status"}, + ) + + // BridgeTransactionsPending tracks pending transactions + BridgeTransactionsPending = prometheus.NewGauge( + prometheus.GaugeOpts{ + Name: "bridge_transactions_pending", + Help: "Number of pending bridge transactions", + }, + ) + + // BridgeTransactionsCompleted tracks completed transactions + BridgeTransactionsCompleted = prometheus.NewGauge( + prometheus.GaugeOpts{ + Name: "bridge_transactions_completed", + Help: "Number of completed bridge transactions", + }, + ) + + // LastProcessedBlock tracks last processed block per chain + LastProcessedBlock = prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "bridge_last_processed_block", + Help: "Last processed block number per chain", + }, + []string{"chain_id"}, + ) + + // OldestPendingTimestamp tracks the unix timestamp of the oldest pending transaction + OldestPendingTimestamp = prometheus.NewGauge( + prometheus.GaugeOpts{ + Name: "bridge_oldest_pending_timestamp", + Help: "Unix timestamp of the oldest pending bridge transaction (0 if none)", + }, + ) + + registerOnce sync.Once + + // pendingTxTimes tracks when each bridgeID entered pending state + pendingMu sync.Mutex + pendingTxTimes = map[string]float64{} +) + +// Register registers all Prometheus metrics +func Register() { + registerOnce.Do(func() { + prometheus.MustRegister( + BridgeTransactionsTotal, + BridgeTransactionsPending, + BridgeTransactionsCompleted, + LastProcessedBlock, + OldestPendingTimestamp, + ) + }) +} + +// StartServer starts the Prometheus metrics HTTP server +func StartServer(ctx context.Context, port int) error { + Register() + + mux := http.NewServeMux() + mux.Handle("/metrics", promhttp.Handler()) + + mux.HandleFunc("/health", func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte("OK")) + }) + + server := &http.Server{ + Addr: fmt.Sprintf(":%d", port), + Handler: mux, + } + + go func() { + <-ctx.Done() + _ = server.Shutdown(context.Background()) + }() + + log.Info().Int("port", port).Msg("Starting metrics server") + + return server.ListenAndServe() +} + +// TrackPending records a bridgeID as pending +func TrackPending(bridgeID string) { + pendingMu.Lock() + defer pendingMu.Unlock() + + pendingTxTimes[bridgeID] = float64(time.Now().Unix()) + updateOldestPending() +} + +// ResolvePending removes a bridgeID from pending tracking +func ResolvePending(bridgeID string) { + pendingMu.Lock() + defer pendingMu.Unlock() + + delete(pendingTxTimes, bridgeID) + updateOldestPending() +} + +// updateOldestPending sets the gauge to the oldest pending timestamp +func updateOldestPending() { + if len(pendingTxTimes) == 0 { + OldestPendingTimestamp.Set(0) + return + } + + var oldest float64 + for _, ts := range pendingTxTimes { + if oldest == 0 || ts < oldest { + oldest = ts + } + } + + OldestPendingTimestamp.Set(oldest) +} diff --git a/application/receipt.go b/application/receipt.go index 8cc0a6f..328f4e9 100644 --- a/application/receipt.go +++ b/application/receipt.go @@ -1,28 +1,18 @@ package application -import ( - "github.com/0xAtelerix/sdk/gosdk/apptypes" - "github.com/holiman/uint256" -) +import "github.com/0xAtelerix/sdk/gosdk/apptypes" -var _ apptypes.Receipt = &Receipt{} - -//nolint:errname // Receipt is not an error type, it just implements Error() method for interface compliance +// Receipt is a minimal stub to satisfy the SDK's Receipt interface. +// This bridge app doesn't use receipts - bridge events are stored directly. +// +//nolint:errname // Name must be Receipt to implement apptypes.Receipt interface type Receipt struct { - // Base receipt fields - TxnHash [32]byte `json:"tx_hash"` - ErrorMessage string `json:"error,omitempty"` - TxStatus apptypes.TxReceiptStatus `json:"tx_status"` - - // Additional fields based on txn - Sender string `json:"sender"` - SenderBalance *uint256.Int `json:"sender_balance"` - Receiver string `json:"receiver"` - ReceiverBalance *uint256.Int `json:"receiver_balance"` - Token string `json:"token"` - Value uint64 `json:"value"` + TxnHash [32]byte `json:"txHash"` + TxStatus apptypes.TxReceiptStatus `json:"status"` } +var _ apptypes.Receipt = &Receipt{} + func (r Receipt) TxHash() [32]byte { return r.TxnHash } @@ -31,6 +21,6 @@ func (r Receipt) Status() apptypes.TxReceiptStatus { return r.TxStatus } -func (r Receipt) Error() string { - return r.ErrorMessage +func (Receipt) Error() string { + return "" } diff --git a/application/solana_payload.go b/application/solana_payload.go deleted file mode 100644 index c24a972..0000000 --- a/application/solana_payload.go +++ /dev/null @@ -1,139 +0,0 @@ -package application - -import ( - "encoding/binary" - "fmt" - - "github.com/0xAtelerix/sdk/gosdk/apptypes" - "github.com/0xAtelerix/sdk/gosdk/external" - "github.com/0xAtelerix/sdk/gosdk/library" - "github.com/blocto/solana-go-sdk/common" - "github.com/blocto/solana-go-sdk/types" -) - -// NOTE: This implementation is specific to the Solana programs in gosdk/solana-programs deployed on Devnet -// If you're using your own Solana program, you'll need to modify: -// 1. The account structure (number and order of accounts) -// 2. The data payload format -// 3. PDA derivation logic if using different seeds -// 4. Program IDs and other constants - -const ( - AppchainProgIDStr = "33ZWF8uKZd4GgsXKMmHNnCMu82MW2yGe1gkTE27YC8Ke" // Deployed on solana devnet - MintPubkeyStr = "sN8167EqsyWten98aTVd4N138GjnZZDEGjX6MhZQfC8" - TokenProgID = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" //nolint:gosec // This is Solana's SPL Token Program ID, not a credential - XUserPubkeyStr = "E3Ct6ynJFr8y3bYWcQ8NeiugWLPFqPvRbfXRQvcqZn4M" -) - -// deriveATA derives the Associated Token Account address for a given mint and owner -func deriveATA(mint, owner string) string { - mintPubkey := common.PublicKeyFromString(mint) - ownerPubkey := common.PublicKeyFromString(owner) - tokenProgramID := common.PublicKeyFromString(TokenProgID) - - // Find PDA for ATA: find_program_address([owner, TOKEN_PROGRAM_ID, mint], ASSOCIATED_TOKEN_PROGRAM_ID) - // Using the standard Solana Associated Token Program ID - associatedTokenProgramID := common.SPLAssociatedTokenAccountProgramID - - seeds := [][]byte{ - ownerPubkey.Bytes(), - tokenProgramID.Bytes(), - mintPubkey.Bytes(), - } - - ata, _, err := common.FindProgramAddress(seeds, associatedTokenProgramID) - if err != nil { - // Fallback to placeholder if derivation fails - return "DerivedATAPubkeyHere" - } - - return ata.ToBase58() -} - -// deriveMintAuthorityPDA derives the PDA for mint authority using the appchain program ID -func deriveMintAuthorityPDA(programID string) (string, uint8) { - progPubkey := common.PublicKeyFromString(programID) - seeds := [][]byte{[]byte("mint_authority")} - - pda, bump, err := common.FindProgramAddress(seeds, progPubkey) - if err != nil { - // Should never happen with valid inputs - return "", 0 - } - - return pda.ToBase58(), bump -} - -// createSolanaMintPayload creates a cross-chain transaction payload for minting tokens on Solana. -// This implementation is specific to the SDK's Solana program (gosdk/solana-programs) which: -// - Expects exactly 5 accounts in this order: -// 1. Appchain Program (the program itself) -// 2. Mint Account (the token mint) -// 3. Associated Token Account (recipient's ATA) -// 4. Mint Authority PDA -// 5. Token Program -// -// - Requires payload data in format: recipient pubkey (32 bytes) + amount (8 bytes, little-endian) -// -// If you're using a custom Solana program, you'll need to modify this function to match your program's: -// - Account structure and ordering -// - Instruction data format -// - PDA derivation logic (if using different seeds) -// - Access control and signing requirements -func createSolanaMintPayload(amount uint64) (apptypes.ExternalTransaction, error) { - // Build Solana mint payload - appchainProg := types.AccountMeta{ - PubKey: common.PublicKeyFromString(AppchainProgIDStr), - IsSigner: false, - IsWritable: false, - } - mintAcc := types.AccountMeta{ - PubKey: common.PublicKeyFromString(MintPubkeyStr), - IsSigner: false, - IsWritable: true, // Mint authority writable - } - ataAcc := types.AccountMeta{ - PubKey: common.PublicKeyFromString(deriveATA(MintPubkeyStr, XUserPubkeyStr)), - IsSigner: false, - IsWritable: true, // Recipient ATA - } - - // Add mint authority PDA (4th specific account) - authorityPDA, _ := deriveMintAuthorityPDA(AppchainProgIDStr) - authorityAcc := types.AccountMeta{ - PubKey: common.PublicKeyFromString(authorityPDA), - IsSigner: false, // Signed via seeds in appchain - IsWritable: false, - } - - tokenProg := types.AccountMeta{ - PubKey: common.PublicKeyFromString(TokenProgID), - IsSigner: false, - IsWritable: false, - } - - // Appchain-specific data: recipient (32 bytes) + amount (8 bytes) - // Total: 40 bytes (matches Rust program format) - appchainData := make([]byte, 0, 32+8) - - // 1. Add recipient (32 bytes) - recipientPubkey := common.PublicKeyFromString(XUserPubkeyStr) - appchainData = append(appchainData, recipientPubkey.Bytes()...) - - // 2. Add amount (8 bytes, little-endian) - amountBytes := make([]byte, 8) - binary.LittleEndian.PutUint64(amountBytes, amount) - appchainData = append(appchainData, amountBytes...) - - // Build account list: appchainProg first, then specifics (mint, ata, authority, token) - // Total: 5 accounts will be passed to appchain after CPI - // In appchain: [0]=program, [1]=mint, [2]=ata, [3]=authority, [4]=token - exTx, err := external.NewExTxBuilder(appchainData, library.SolanaDevnetChainID). - AddSolanaAccounts([]types.AccountMeta{appchainProg, mintAcc, ataAcc, authorityAcc, tokenProg}). - Build() - if err != nil { - return apptypes.ExternalTransaction{}, fmt.Errorf("build payload: %w", err) - } - - return exTx, nil -} diff --git a/application/state.go b/application/state.go new file mode 100644 index 0000000..d9e326e --- /dev/null +++ b/application/state.go @@ -0,0 +1,31 @@ +package application + +import ( + "context" + "encoding/json" + + "github.com/ledgerwatch/erigon-lib/kv" +) + +// GetBridgeEvent retrieves a bridge event by ID +func GetBridgeEvent(ctx context.Context, db kv.RoDB, bridgeID string) (*BridgeEvent, error) { + var event BridgeEvent + + err := db.View(ctx, func(tx kv.Tx) error { + data, err := tx.GetOne(BridgeEventsBucket, []byte(bridgeID)) + if err != nil { + return err + } + + if len(data) == 0 { + return ErrBridgeNotFound + } + + return json.Unmarshal(data, &event) + }) + if err != nil { + return nil, err + } + + return &event, nil +} diff --git a/application/subscription.go b/application/subscription.go new file mode 100644 index 0000000..180ce82 --- /dev/null +++ b/application/subscription.go @@ -0,0 +1,35 @@ +package application + +import ( + "github.com/0xAtelerix/sdk/gosdk" + "github.com/0xAtelerix/sdk/gosdk/apptypes" + "github.com/0xAtelerix/sdk/gosdk/library" + "github.com/rs/zerolog/log" +) + +// Event signatures for subscription +const ( + BridgeInitiatedSignature = "BridgeInitiated(bytes32,uint256,uint256,address,uint256,address,address)" + AssetClaimedSignature = "AssetClaimed(bytes32,address,address,uint256)" +) + +// SubscribeBridgeContracts registers the bridge contracts and events with the subscriber +// so that ProcessBlock receives blocks containing events from these contracts. +func SubscribeBridgeContracts(subscriber *gosdk.Subscriber, cfg *AppConfig) { + contracts := cfg.Bridge.GetBridgeContracts() + + for chainID, contractAddr := range contracts { + subscriber.SubscribeEthContract( + apptypes.ChainType(chainID), + library.EthereumAddress(contractAddr), + nil, + library.EventTopic(BridgeInitiatedSignature), + library.EventTopic(AssetClaimedSignature), + ) + + log.Info(). + Uint64("chainID", chainID). + Str("contract", contractAddr.Hex()). + Msg("Subscribed to bridge contract") + } +} diff --git a/application/transaction.go b/application/transaction.go index bd6f408..c4e2782 100644 --- a/application/transaction.go +++ b/application/transaction.go @@ -1,182 +1,34 @@ package application import ( - "encoding/hex" - "encoding/json" - "math/big" - "strings" - "github.com/0xAtelerix/sdk/gosdk/apptypes" - "github.com/0xAtelerix/sdk/gosdk/external" - "github.com/0xAtelerix/sdk/gosdk/library" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" - "github.com/holiman/uint256" "github.com/ledgerwatch/erigon-lib/kv" ) -var _ apptypes.AppTransaction[Receipt] = &Transaction[Receipt]{} - -func AccountKey(sender string, token string) []byte { - return []byte(token + sender) +// Transaction is a minimal stub to satisfy the SDK's AppTransaction interface. +// This bridge app doesn't use appchain transactions - all processing happens +// via external chain events in ExtBlockProcessor. +// +//nolint:recvcheck // Mixed receivers required: Unmarshal needs pointer, others need value for interface +type Transaction struct { + TxHash [32]byte `json:"hash"` } -type Transaction[R Receipt] struct { - Sender string `json:"sender"` - Value uint64 `json:"value"` - Receiver string `json:"receiver"` - Token string `json:"token"` - TxHash string `json:"hash"` - GenerateExtTxn bool `json:"generate_ext_txn,omitempty"` // When true, generates an external transaction to Sepolia -} - -func (e *Transaction[R]) Unmarshal(b []byte) error { - return json.Unmarshal(b, e) -} - -func (e Transaction[R]) Marshal() ([]byte, error) { - return json.Marshal(e) -} - -func (e Transaction[R]) Hash() [32]byte { - // Remove all hex prefixes (handles cases like "0x0x..." or "0X0x...") - txHash := e.TxHash - for strings.HasPrefix(strings.ToLower(txHash), "0x") { - txHash = txHash[2:] - } - - hashBytes, err := hex.DecodeString(txHash) - if err != nil { - panic(err) - } - - var h [32]byte - copy(h[:], hashBytes) - - return h -} - -func (e Transaction[R]) Process( - dbTx kv.RwTx, -) (res R, txs []apptypes.ExternalTransaction, err error) { - var senderBalanceData []byte - - senderTokenKey := AccountKey(e.Sender, e.Token) - - senderBalanceData, err = dbTx.GetOne(AccountsBucket, senderTokenKey) - if err != nil { - return e.failedReceipt(err), nil, nil - } - - if len(senderBalanceData) == 0 { - return e.failedReceipt(ErrNotEnoughBalance), nil, nil - } - - senderBalance := &uint256.Int{} - senderBalance.SetBytes(senderBalanceData) +var _ apptypes.AppTransaction[Receipt] = &Transaction{} - if senderBalance.CmpUint64(e.Value) < 0 { - return e.failedReceipt(ErrNotEnoughBalance), nil, nil - } - - var receiverBalanceData []byte - - receiverTokenKey := AccountKey(e.Receiver, e.Token) - - receiverBalanceData, err = dbTx.GetOne(AccountsBucket, receiverTokenKey) - if err != nil { - return e.failedReceipt(err), nil, nil - } - - receiverBalance := &uint256.Int{} - receiverBalance.SetBytes(receiverBalanceData) - - amount := uint256.NewInt(e.Value) - - receiverBalance.Add(receiverBalance, amount) - senderBalance.Sub(senderBalance, amount) - - err = dbTx.Put(AccountsBucket, senderTokenKey, senderBalance.Bytes()) - if err != nil { - return e.failedReceipt(err), nil, nil - } - - err = dbTx.Put(AccountsBucket, receiverTokenKey, receiverBalance.Bytes()) - if err != nil { - return e.failedReceipt(err), nil, nil - } - - res = e.successReceipt(senderBalance, receiverBalance) - - // Generate external transaction if requested - var externalTxs []apptypes.ExternalTransaction - - if e.GenerateExtTxn { - extTx, err := e.createExternalTransaction() - if err != nil { - // Log error but don't fail the transaction - return e.failedReceipt(err), nil, nil - } - - externalTxs = append(externalTxs, extTx) - } - - return res, externalTxs, nil +func (*Transaction) Unmarshal(_ []byte) error { + return nil } -func (e *Transaction[R]) failedReceipt(err error) R { - return R{ - TxnHash: e.Hash(), - Sender: e.Sender, - Receiver: e.Receiver, - Token: e.Token, - Value: e.Value, - ErrorMessage: err.Error(), - TxStatus: apptypes.ReceiptFailed, - } +func (Transaction) Marshal() ([]byte, error) { + return nil, nil } -func (e *Transaction[R]) successReceipt(senderBalance, receiverBalance *uint256.Int) R { - return R{ - TxnHash: e.Hash(), - Sender: e.Sender, - SenderBalance: senderBalance, - Receiver: e.Receiver, - ReceiverBalance: receiverBalance, - Token: e.Token, - Value: e.Value, - TxStatus: apptypes.ReceiptConfirmed, - } +func (t Transaction) Hash() [32]byte { + return t.TxHash } -// createExternalTransaction creates an external transaction to Sepolia -// with a payload that matches the AppChain contract format -func (e *Transaction[R]) createExternalTransaction() (apptypes.ExternalTransaction, error) { - // Parse receiver as Ethereum address - // If it's not a valid address, create a deterministic address from the string - var recipientAddr common.Address - if common.IsHexAddress(e.Receiver) { - recipientAddr = common.HexToAddress(e.Receiver) - } else { - // For non-address receivers (like "alice", "bob"), create a deterministic address - // by hashing the name using Keccak256 and taking the last 20 bytes - hash := crypto.Keccak256Hash([]byte(e.Receiver)) - recipientAddr = common.BytesToAddress(hash.Bytes()) - } - - // Convert value to big.Int - amount := big.NewInt(int64(e.Value)) - - // Create payload matching AppChain contract format using the existing function - // Format: [recipient:20bytes][amount:32bytes][tokenName:variable] - // This matches the contract in 0xAtelerix/sdk/contracts/pelacli/AppChain.sol - payload := createTokenMintPayload(recipientAddr, amount, e.Token) - - // Create external transaction targeting Sepolia - extTx, err := external.NewExTxBuilder(payload, library.EthereumSepoliaChainID).Build() - if err != nil { - return apptypes.ExternalTransaction{}, err - } - - return extTx, nil +// Process is a no-op - bridge processing happens via ExtBlockProcessor.ProcessBlock() +func (t Transaction) Process(_ kv.RwTx) (Receipt, []apptypes.ExternalTransaction, error) { + return Receipt{TxnHash: t.TxHash, TxStatus: apptypes.ReceiptConfirmed}, nil, nil } diff --git a/cmd/main.go b/cmd/main.go index 22a1b29..e7eacb5 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -14,6 +14,7 @@ import ( "github.com/0xAtelerix/example/application" "github.com/0xAtelerix/example/application/api" + "github.com/0xAtelerix/example/application/metrics" ) func main() { @@ -23,24 +24,13 @@ func main() { _ = fs.Parse(os.Args[1:]) // Load config from file or use defaults - var ( - cfg *gosdk.InitConfig - err error - ) - - if *configPath != "" { - cfg, err = gosdk.LoadConfig(*configPath) - if err != nil { - log.Fatal().Err(err).Msg("Failed to load config") - } - - log.Info().Str("config", *configPath).Msg("Loaded config") - } else { - cfg = &gosdk.InitConfig{} - - log.Info().Msg("Using default config") + cfg, err := application.LoadConfig(*configPath) + if err != nil { + log.Fatal().Err(err).Msg("Failed to load config") } + log.Info().Msg("Config loaded") + // Setup logging ctx := gosdk.SetupLogger(context.Background(), cfg.LogLevel) @@ -54,23 +44,26 @@ func main() { } // Run starts the appchain with the given config. Exported for testing. -func Run(ctx context.Context, cfg *gosdk.InitConfig) error { +func Run(ctx context.Context, cfg *application.AppConfig) error { // Add custom tables to config cfg.CustomTables = application.Tables() // Stage 1: Initialize storage and config (logger comes from context) - appInit, err := gosdk.InitApp[application.Transaction[application.Receipt]](ctx, *cfg) + appInit, err := gosdk.InitApp[application.Transaction](ctx, cfg.InitConfig) if err != nil { return fmt.Errorf("init storage: %w", err) } defer appInit.Close() + // Subscribe to bridge contracts on external chains + application.SubscribeBridgeContracts(appInit.Storage.Subscriber(), cfg) + // Stage 2: Create appchain with batch processor app := gosdk.NewAppchain( appInit.Storage, appInit.Config, - gosdk.NewDefaultBatchProcessor[application.Transaction[application.Receipt]]( - application.NewExtBlockProcessor(appInit.Storage.Multichain()), + gosdk.NewDefaultBatchProcessor[application.Transaction]( + application.NewExtBlockProcessor(appInit.Storage.Multichain(), cfg), appInit.Storage.Multichain(), appInit.Storage.Subscriber(), ), @@ -82,25 +75,21 @@ func Run(ctx context.Context, cfg *gosdk.InitConfig) error { return fmt.Errorf("init dev validator set: %w", err) } - // Initialize app-specific genesis state - if err := application.InitializeGenesis(ctx, appInit.Storage.AppchainDB()); err != nil { - return fmt.Errorf("init genesis state: %w", err) - } - // Setup JSON-RPC server rpcServer := rpc.NewStandardRPCServer(nil) - rpcServer.AddMiddleware(api.NewExampleMiddleware(log.Logger)) + // Add standard RPC methods for explorer compatibility rpc.AddStandardMethods[ - application.Transaction[application.Receipt], + application.Transaction, application.Receipt, application.Block, ](rpcServer, appInit.Storage.AppchainDB(), appInit.Storage.TxPool(), appInit.Config.ChainID) - api.NewCustomRPC(rpcServer, appInit.Storage.AppchainDB()).AddRPCMethods() + // Add custom bridge RPC methods + api.NewCustomRPC(rpcServer, appInit.Storage.AppchainDB(), cfg).AddRPCMethods() // Error channel for goroutines - errCh := make(chan error, 2) + errCh := make(chan error, 3) // Run appchain in background go func() { @@ -112,6 +101,13 @@ func Run(ctx context.Context, cfg *gosdk.InitConfig) error { errCh <- rpcServer.StartHTTPServer(ctx, appInit.Config.RPCPort) }() + // Run metrics server if configured + if cfg.MetricsPort > 0 { + go func() { + errCh <- metrics.StartServer(ctx, cfg.MetricsPort) + }() + } + // Wait for shutdown signal or error select { case <-ctx.Done(): diff --git a/cmd/main_test.go b/cmd/main_test.go index 74cfe68..b960614 100644 --- a/cmd/main_test.go +++ b/cmd/main_test.go @@ -36,38 +36,46 @@ func waitUntil(ctx context.Context, f func() bool) error { } } -// TestEndToEnd spins up the appchain, posts a transaction to the /rpc endpoint and +// TestEndToEnd spins up the appchain, posts a request to the /rpc endpoint and // verifies we get a 2xx response. func TestEndToEnd(t *testing.T) { port := getFreePort(t) dataDir := t.TempDir() - chainID := uint64(1001) + chainID := uint64(42) - // Create required directories and databases - // SDK's InitApp() expects these paths to exist + // Create required directories txBatchPath := gosdk.TxBatchPathForChain(dataDir, chainID) eventsPath := gosdk.EventsPath(dataDir) require.NoError(t, os.MkdirAll(txBatchPath, 0o755)) require.NoError(t, os.MkdirAll(eventsPath, 0o755)) - // Create TxBatchDB (normally created by pelacli's fetcher) + // Create empty TxBatchDB err := createEmptyMDBXDatabase(txBatchPath, gosdk.TxBucketsTables()) require.NoError(t, err, "create empty txBatch database") - // Create config with test values - cfg := &gosdk.InitConfig{ - ChainID: &chainID, - DataDir: dataDir, - EmitterPort: ":0", // Let OS choose - RPCPort: fmt.Sprintf(":%d", port), - RequiredChains: []uint64{}, + // Create test config with embedded SDK config and bridge config + cfg := &application.AppConfig{ + InitConfig: gosdk.InitConfig{ + ChainID: &chainID, + DataDir: dataDir, + EmitterPort: ":0", + RPCPort: fmt.Sprintf(":%d", port), + RequiredChains: []uint64{}, + CustomTables: application.Tables(), + }, + Bridge: application.BridgeConfig{ + Contracts: map[uint64]string{}, + TokenMappings: map[uint64]map[string]string{}, + }, } - // Create cancellable context for the test ctx, cancel := context.WithCancel(t.Context()) defer cancel() + // Setup logger + ctx = gosdk.SetupLogger(ctx, 1) // Info level + // Run appchain in background go func() { if runErr := Run(ctx, cfg); runErr != nil { @@ -75,40 +83,36 @@ func TestEndToEnd(t *testing.T) { } }() - // Wait until HTTP service is up + // Wait for HTTP service rpcURL := fmt.Sprintf("http://127.0.0.1:%d/rpc", port) waitCtx, waitCancel := context.WithTimeout(ctx, 5*time.Second) defer waitCancel() err = waitUntil(waitCtx, func() bool { - req, reqErr := http.NewRequestWithContext(waitCtx, http.MethodGet, rpcURL, nil) - if reqErr != nil { - return false - } + req, _ := http.NewRequestWithContext(waitCtx, http.MethodGet, rpcURL, nil) resp, respErr := http.DefaultClient.Do(req) if respErr != nil { return false } - _ = resp.Body.Close() + resp.Body.Close() return true }) require.NoError(t, err, "JSON-RPC service never became ready") - // Build & send a transaction - tx := application.Transaction[application.Receipt]{ - Sender: "Vasya", - Value: 42, - TxHash: "deadbeef", + // Test getBridgeStatus RPC method + rpcRequest := map[string]any{ + "jsonrpc": "2.0", + "method": "getBridgeStatus", + "params": []any{map[string]string{"bridgeId": "0x1234"}}, + "id": 1, } var buf bytes.Buffer - - err = json.NewEncoder(&buf).Encode(tx) - require.NoError(t, err, "encode tx") + require.NoError(t, json.NewEncoder(&buf).Encode(rpcRequest)) req, err := http.NewRequestWithContext( waitCtx, @@ -127,13 +131,22 @@ func TestEndToEnd(t *testing.T) { require.NoError(t, resp.Body.Close()) }() - require.True(t, resp.StatusCode >= 200 && resp.StatusCode < 300, - "unexpected HTTP status: %s", resp.Status) + require.True( + t, + resp.StatusCode >= 200 && resp.StatusCode < 300, + "unexpected HTTP status: %s", + resp.Status, + ) - // Cancel context to trigger graceful shutdown - cancel() + // Verify we get a valid JSON-RPC response (error expected since bridge doesn't exist) + var rpcResp map[string]any - // Give Run() a moment to tear down + err = json.NewDecoder(resp.Body).Decode(&rpcResp) + require.NoError(t, err, "decode rpc response") + // We expect an error since the bridge doesn't exist, but the RPC endpoint works + require.NotNil(t, rpcResp["error"], "expected error in response for non-existent bridge") + + cancel() time.Sleep(500 * time.Millisecond) t.Log("Success!") @@ -158,7 +171,7 @@ func getFreePort(t *testing.T) int { return port } -// createEmptyMDBXDatabase creates an empty MDBX database that can be opened in readonly mode. +// createEmptyMDBXDatabase creates an empty MDBX database that can be opened in readonly mode func createEmptyMDBXDatabase(dbPath string, tableCfg kv.TableCfg) error { tempDB, err := mdbx.NewMDBX(mdbxlog.New()). Path(dbPath). diff --git a/config.example.yaml b/config.example.yaml deleted file mode 100644 index 23b191e..0000000 --- a/config.example.yaml +++ /dev/null @@ -1,50 +0,0 @@ -# Appchain Configuration -# Copy this file to config.yaml and modify as needed. -# All fields are optional - defaults will be used if not specified. -# Run with: ./appchain --config=config.yaml - -# Unique identifier for this appchain -# Must match the chain_id in pelacli's appchains config -# Default: 42 -chain_id: 42 - -# Root data directory shared with pelacli -# Both appchain and pelacli must use the same data_dir for communication -# Default: /data (container path, works with docker-compose default mounts) -data_dir: "/data" - -# gRPC port for emitter API (pelacli fetcher connects here) -# Default: :9090 -emitter_port: ":9090" - -# HTTP port for JSON-RPC server (clients connect here) -# Default: :8080 -rpc_port: ":8080" - -# Log level: -1=trace, 0=debug, 1=info, 2=warn, 3=error -# Default: 1 (info) -log_level: 1 - -# Required external chains that this appchain reads from -# Appchain will wait for these chains' data at startup -# Common chain IDs: 11155111 (Eth Sepolia), 80002 (Polygon Amoy), 97 (BSC testnet) -required_chains: - - 80002 # Polygon Amoy testnet - - 11155111 # Ethereum Sepolia testnet - -# Prometheus metrics port (optional) -# Default: "" (disabled) -# prometheus_port: ":2112" - -# Validator ID for consensus (optional) -# Default: "" (uses default validator) -# validator_id: "validator-1" - -# Custom paths (advanced use only - overrides standard data_dir structure) -# NOTE: Appchain and pelacli must use compatible paths for communication -# custom_paths: -# multichain_dir: "/custom/path/to/multichain" -# appchain_db_dir: "/custom/path/to/appchain/db" -# txpool_dir: "/custom/path/to/txpool" -# events_stream_dir: "/custom/path/to/events" -# txbatch_dir: "/custom/path/to/txbatch" diff --git a/config.yaml b/config.yaml new file mode 100644 index 0000000..f2da7a6 --- /dev/null +++ b/config.yaml @@ -0,0 +1,28 @@ +chain_id: 42 + +data_dir: "/data" + +emitter_port: ":9090" + +rpc_port: ":8080" + +metrics_port: 9091 + +log_level: 1 + +required_chains: + - 11155111 # Ethereum Sepolia testnet + - 50591822 # Stavanger testnet + +bridge: + contracts: + 11155111: "0x844E740Ea7F404c6208fd85Ee6114a14F8037df7" # Sepolia + 50591822: "0x3C1c8351a09DB0300786148B56EcB7be2FaA322e" # Stavanger + + token_mappings: + # Sepolia -> Stavanger: POL ERC20 -> native + 11155111: + "0x6a7c3f4b0651d6da389ad1d11d962ea458cdca70": "0x0000000000000000000000000000000000000000" + # Stavanger -> Sepolia: native -> POL ERC20 + 50591822: + "0x0000000000000000000000000000000000000000": "0x6a7c3f4b0651d6da389ad1d11d962ea458cdca70" diff --git a/contracts/.env.example b/contracts/.env.example new file mode 100644 index 0000000..1c70f21 --- /dev/null +++ b/contracts/.env.example @@ -0,0 +1,32 @@ +# RPC Endpoints +# Sepolia Testnet (L1) +SEPOLIA_RPC_URL=https://sepolia.infura.io/v3/YOUR_INFURA_KEY + +# Stavanger Rollup (L2 - Custom rollup with native POL) +STAVANGER_RPC_URL=https://stavanger-rpc.eu-north-2.gateway.fm + +# Deployment +# This account needs ETH on Sepolia and POL on Stavanger +PRIVATE_KEY=your_private_key_here + +# Block Explorer API Keys (for verification) +ETHERSCAN_API_KEY=your_etherscan_api_key + +# Stavanger Explorer (Blockscout) +STAVANGER_EXPLORER_API_KEY=none +STAVANGER_EXPLORER_API_URL=https://stavanger-blockscout.eu-north-2.gateway.fm/api +STAVANGER_EXPLORER_URL=https://stavanger-blockscout.eu-north-2.gateway.fm + +# Contract Configuration +VALIDATOR_ADDRESS=0x... # pelacli validator address (should match PRIVATE_KEY address) + +# Pelagos Contract Addresses (set after deployment if available) +PELAGOS_SEPOLIA=0x... +PELAGOS_STAVANGER=0x... + +# Example Token Addresses for Testing +# The bridge is generic and supports ANY ERC20 token or native gas token +# Sepolia: POL ERC20 token (example for testing) +POL_TOKEN_SEPOLIA=0x6a7c3f4b0651d6da389ad1d11d962ea458cdca70 +# Stavanger: Use address(0) for native POL +# You can bridge any ERC20 deployed on either chain diff --git a/contracts/.gitignore b/contracts/.gitignore new file mode 100644 index 0000000..e8330a1 --- /dev/null +++ b/contracts/.gitignore @@ -0,0 +1,14 @@ +node_modules +.env +coverage +coverage.json +typechain +typechain-types + +# Hardhat files +cache +artifacts + +# Deployments (keep config but ignore timestamped deployments) +deployments/*.json +!deployments/.gitkeep diff --git a/contracts/contracts/Bridge.sol b/contracts/contracts/Bridge.sol new file mode 100644 index 0000000..c5f0538 --- /dev/null +++ b/contracts/contracts/Bridge.sol @@ -0,0 +1,434 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; +import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; +import "@openzeppelin/contracts/utils/Pausable.sol"; + +/** + * @title Bridge + * @notice Generic cross-chain bridge supporting any ERC20 token or native gas token + * @dev Integrates with Pelagos SDK - implements executeTransaction(bytes) for cross-chain calls + * + * Architecture: + * - Supports any ERC20 token on any chain + * - Native gas token represented by address(0) + * - Pelagos.sol calls executeTransaction() to complete bridge on destination + * + * Flow: + * Source -> Destination: + * 1. User calls bridgeAsset(token, amount, destChainId, recipient) + * 2. For ERC20: tokens locked in bridge, for native: msg.value locked + * 3. BridgeInitiated event emitted + * 4. pelacli monitors event, processes in appchain + * 5. Appchain generates ExternalTransaction with binary payload + * 6. pelacli submits to Pelagos.sol on destination chain + * 7. Pelagos.sol calls Bridge.executeTransaction(payload) + * 8. Bridge unlocks tokens to recipient + * + * Token Identification: + * - address(0) = native gas token (ETH, POL, etc.) + * - Any other address = ERC20 token contract + */ +contract Bridge is Ownable, ReentrancyGuard, Pausable { + using SafeERC20 for IERC20; + + // ============ State Variables ============ + + /// @notice Pelagos contract address (authorized to call executeTransaction) + address public pelagosContract; + + /// @notice Current chain ID + uint256 public immutable chainId; + + /// @notice Nonce for generating unique bridge IDs + uint256 public bridgeNonce; + + /// @notice Mapping of bridge ID to whether it has been claimed + mapping(bytes32 => bool) public claimedBridges; + + /// @notice Mapping of bridge ID to bridge details + mapping(bytes32 => BridgeData) public bridges; + + // ============ Structs ============ + + struct BridgeData { + uint256 sourceChainId; + uint256 destChainId; + address token; + uint256 amount; + address sender; + address recipient; + uint256 timestamp; + bool claimed; + } + + // ============ Events ============ + + /** + * @notice Emitted when a bridge is initiated + * @param bridgeId Unique identifier for this bridge transaction + * @param sourceChainId Chain ID where bridge was initiated + * @param destChainId Target chain ID + * @param token Token address (address(0) for native) + * @param amount Amount of tokens + * @param sender Original sender on source chain + * @param recipient Recipient address on destination chain + */ + event BridgeInitiated( + bytes32 indexed bridgeId, + uint256 indexed sourceChainId, + uint256 indexed destChainId, + address token, + uint256 amount, + address sender, + address recipient + ); + + /** + * @notice Emitted when tokens are claimed on destination chain + * @param bridgeId Unique identifier for this bridge transaction + * @param recipient Recipient who received tokens + * @param token Token address (address(0) for native) + * @param amount Amount claimed + */ + event AssetClaimed( + bytes32 indexed bridgeId, + address indexed recipient, + address indexed token, + uint256 amount + ); + + /** + * @notice Emitted when Pelagos contract is updated + */ + event PelagosContractUpdated(address indexed oldPelagos, address indexed newPelagos); + + // ============ Errors ============ + + error InvalidDestinationChain(); + error InvalidRecipient(); + error InvalidToken(); + error InvalidAmount(); + error InvalidBridgeId(); + error BridgeAlreadyClaimed(); + error InsufficientBalance(); + error Unauthorized(); + error PayloadDecodingFailed(); + error NativeTransferFailed(); + error PermitFailed(); + + // ============ Modifiers ============ + + /** + * @notice Only Pelagos contract can call executeTransaction + */ + modifier onlyPelagos() { + if (msg.sender != pelagosContract) revert Unauthorized(); + _; + } + + // ============ Constructor ============ + + /** + * @notice Initialize bridge contract + */ + constructor() Ownable(msg.sender) { + chainId = block.chainid; + } + + // ============ External Functions ============ + + /** + * @notice Bridge tokens to destination chain + * @param token Token address to bridge (address(0) for native gas token) + * @param amount Amount of tokens to bridge + * @param destChainId Destination chain ID + * @param recipient Recipient address on destination chain + * @param permitData Optional EIP-2612 permit data for gasless approval (empty for pre-approved or native tokens) + * @return bridgeId Unique identifier for this bridge transaction + * + * @dev For native tokens: Send value with transaction (msg.value must equal amount) + * For ERC20 tokens: Either approve beforehand OR provide permitData for one-transaction bridging + * + * permitData format (if using permit): + * - Encode: abi.encode(owner, spender, value, deadline, v, r, s) + * - This executes permit() before transferFrom, enabling one-transaction bridging UX + */ + function bridgeAsset( + address token, + uint256 amount, + uint256 destChainId, + address recipient, + bytes calldata permitData + ) external payable nonReentrant whenNotPaused returns (bytes32 bridgeId) { + // Validation + if (destChainId == chainId) revert InvalidDestinationChain(); + if (recipient == address(0)) revert InvalidRecipient(); + if (amount == 0) revert InvalidAmount(); + + if (token == address(0)) { + // Native gas token (ETH, POL, etc.) + if (msg.value != amount) revert InvalidAmount(); + // Native token is already received via msg.value + } else { + // ERC20 token + // No msg.value should be sent for ERC20 + if (msg.value != 0) revert InvalidAmount(); + + // If permit data is provided, execute permit for gasless approval + if (permitData.length > 0) { + try this._executePermit(token, permitData) { + // Permit executed successfully + } catch { + // Permit failed - user should have pre-approved or permit already used + // Continue with transferFrom, which will revert if no allowance + } + } + + // Transfer tokens from user to bridge + IERC20(token).safeTransferFrom(msg.sender, address(this), amount); + } + + // Generate unique bridge ID + bridgeId = keccak256( + abi.encodePacked( + chainId, + destChainId, + token, + amount, + msg.sender, + recipient, + bridgeNonce, + block.timestamp + ) + ); + + // Increment nonce + bridgeNonce++; + + // Store bridge data + bridges[bridgeId] = BridgeData({ + sourceChainId: chainId, + destChainId: destChainId, + token: token, + amount: amount, + sender: msg.sender, + recipient: recipient, + timestamp: block.timestamp, + claimed: false + }); + + // Emit event (monitored by pelacli) + emit BridgeInitiated( + bridgeId, + chainId, + destChainId, + token, + amount, + msg.sender, + recipient + ); + } + + /** + * @notice Execute cross-chain transaction from Pelagos + * @param payload Binary payload containing bridge claim data + * @dev Called by Pelagos.sol when processing external transaction from appchain + * + * Payload structure (160 bytes): + * - Bytes 0-31: bridgeId (bytes32) + * - Bytes 32-63: sourceChainId (uint256) + * - Bytes 64-95: token (address, LEFT-aligned: [20_bytes_address][12_bytes_zeros]) + * - Bytes 96-127: amount (uint256) + * - Bytes 128-159: recipient (address, LEFT-aligned: [20_bytes_address][12_bytes_zeros]) + * + * Note: Addresses are LEFT-aligned so shr(96) extracts them correctly + * Example: [0xABCD...][000...] -> shr(96) -> [000...][0xABCD...] -> address(0xABCD...) + */ + function executeTransaction(bytes calldata payload) external onlyPelagos nonReentrant { + // Decode payload using assembly for gas efficiency + bytes32 bridgeId; + uint256 sourceChainId; + address token; + uint256 amount; + address recipient; + + // Validate payload length (160 bytes = 5 * 32 bytes) + if (payload.length != 160) revert PayloadDecodingFailed(); + + assembly { + // bridgeId: bytes 0-31 + bridgeId := calldataload(payload.offset) + + // sourceChainId: bytes 32-63 + sourceChainId := calldataload(add(payload.offset, 32)) + + // token: bytes 64-95 (address LEFT-aligned, extract with shr(96)) + token := shr(96, calldataload(add(payload.offset, 64))) + + // amount: bytes 96-127 + amount := calldataload(add(payload.offset, 96)) + + // recipient: bytes 128-159 (address LEFT-aligned, extract with shr(96)) + recipient := shr(96, calldataload(add(payload.offset, 128))) + } + + // Validate + if (bridgeId == bytes32(0)) revert InvalidBridgeId(); + if (recipient == address(0)) revert InvalidRecipient(); + if (amount == 0) revert InvalidAmount(); + + // Check if already claimed + if (claimedBridges[bridgeId]) revert BridgeAlreadyClaimed(); + + // Mark as claimed + claimedBridges[bridgeId] = true; + + // Transfer tokens to recipient + if (token == address(0)) { + // Send native gas token + if (address(this).balance < amount) revert InsufficientBalance(); + (bool success, ) = recipient.call{value: amount}(""); + if (!success) revert NativeTransferFailed(); + } else { + // Transfer ERC20 token + if (IERC20(token).balanceOf(address(this)) < amount) revert InsufficientBalance(); + IERC20(token).safeTransfer(recipient, amount); + } + + // Emit event + emit AssetClaimed(bridgeId, recipient, token, amount); + } + + // ============ Admin Functions ============ + + /** + * @notice Set Pelagos contract address + * @param _pelagosContract Address of Pelagos contract + */ + function setPelagosContract(address _pelagosContract) external onlyOwner { + if (_pelagosContract == address(0)) revert InvalidRecipient(); + address old = pelagosContract; + pelagosContract = _pelagosContract; + emit PelagosContractUpdated(old, _pelagosContract); + } + + /** + * @notice Pause bridge operations + */ + function pause() external onlyOwner { + _pause(); + } + + /** + * @notice Unpause bridge operations + */ + function unpause() external onlyOwner { + _unpause(); + } + + /** + * @notice Emergency withdraw of tokens (only owner) + * @param token Token address (address(0) for native) + * @param amount Amount to withdraw + */ + function emergencyWithdraw(address token, uint256 amount) external onlyOwner { + if (token == address(0)) { + // Withdraw native + (bool success, ) = owner().call{value: amount}(""); + if (!success) revert NativeTransferFailed(); + } else { + // Withdraw ERC20 + IERC20(token).safeTransfer(owner(), amount); + } + } + + /** + * @notice Add liquidity to bridge + * @param token Token address (address(0) for native) + * @param amount Amount to add + */ + function addLiquidity(address token, uint256 amount) external payable onlyOwner { + if (token == address(0)) { + // Native token - must send value with transaction + if (msg.value != amount) revert InvalidAmount(); + } else { + // ERC20 token + if (msg.value != 0) revert InvalidAmount(); + IERC20(token).safeTransferFrom(msg.sender, address(this), amount); + } + } + + // ============ Receive Functions ============ + + /** + * @notice Receive native tokens for liquidity + */ + receive() external payable { + // Accept native tokens for liquidity + } + + /** + * @notice Fallback function + */ + fallback() external payable { + revert("Bridge: invalid function call"); + } + + // ============ Internal Functions ============ + + /** + * @notice Execute EIP-2612 permit for gasless approval + * @param token Token contract address + * @param permitData Encoded permit parameters + * @dev This is external only to enable try-catch from bridgeAsset + */ + function _executePermit(address token, bytes calldata permitData) external { + // Only callable by this contract (via try-catch in bridgeAsset) + if (msg.sender != address(this)) revert Unauthorized(); + + // Decode permit parameters + (address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) = + abi.decode(permitData, (address, address, uint256, uint256, uint8, bytes32, bytes32)); + + // Execute permit + IERC20Permit(token).permit(owner, spender, value, deadline, v, r, s); + } + + // ============ View Functions ============ + + /** + * @notice Get bridge data + * @param bridgeId Bridge identifier + * @return Bridge data struct + */ + function getBridge(bytes32 bridgeId) external view returns (BridgeData memory) { + return bridges[bridgeId]; + } + + /** + * @notice Check if bridge has been claimed + * @param bridgeId Bridge identifier + * @return Whether bridge has been claimed + */ + function isClaimed(bytes32 bridgeId) external view returns (bool) { + return claimedBridges[bridgeId]; + } + + /** + * @notice Get contract balance for a specific token + * @param token Token address (address(0) for native) + * @return Balance of specified token + */ + function getBalance(address token) external view returns (uint256) { + if (token == address(0)) { + return address(this).balance; + } else { + return IERC20(token).balanceOf(address(this)); + } + } +} diff --git a/contracts/hardhat.config.js b/contracts/hardhat.config.js new file mode 100644 index 0000000..73d2ac0 --- /dev/null +++ b/contracts/hardhat.config.js @@ -0,0 +1,52 @@ +require("@nomicfoundation/hardhat-toolbox"); +require("dotenv").config(); + +/** @type import('hardhat/config').HardhatUserConfig */ +module.exports = { + solidity: { + version: "0.8.20", + settings: { + optimizer: { + enabled: true, + runs: 200 + } + } + }, + networks: { + hardhat: { + chainId: 31337 + }, + sepolia: { + url: process.env.SEPOLIA_RPC_URL || "", + accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [], + chainId: 11155111 + }, + stavanger: { + url: process.env.STAVANGER_RPC_URL || "https://stavanger-rpc.eu-north-2.gateway.fm", + accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [], + chainId: 50591822 + }, + amoy: { + url: process.env.AMOY_RPC_URL || "https://rpc-amoy.polygon.technology", + accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [], + chainId: 80002 + } + }, + etherscan: { + apiKey: { + sepolia: process.env.ETHERSCAN_API_KEY || "", + stavanger: process.env.STAVANGER_EXPLORER_API_KEY || "none", // May not have explorer + polygonAmoy: process.env.POLYGONSCAN_API_KEY || "" + }, + customChains: [ + { + network: "stavanger", + chainId: 50591822, + urls: { + apiURL: process.env.STAVANGER_EXPLORER_API_URL || "https://stavanger-blockscout.eu-north-2.gateway.fm/api", + browserURL: process.env.STAVANGER_EXPLORER_URL || "https://stavanger-blockscout.eu-north-2.gateway.fm" + } + } + ] + } +}; diff --git a/contracts/package-lock.json b/contracts/package-lock.json new file mode 100644 index 0000000..36bba85 --- /dev/null +++ b/contracts/package-lock.json @@ -0,0 +1,7895 @@ +{ + "name": "pelagos-bridge-contracts", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "pelagos-bridge-contracts", + "version": "1.0.0", + "dependencies": { + "@openzeppelin/contracts": "^5.4.0", + "dotenv": "^17.2.3" + }, + "devDependencies": { + "@nomicfoundation/hardhat-toolbox": "^4.0.0", + "hardhat": "^2.19.0" + } + }, + "node_modules/@adraffy/ens-normalize": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz", + "integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@ethereumjs/rlp": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-5.0.2.tgz", + "integrity": "sha512-DziebCdg4JpGlEqEdGgXmjqcFoJi+JGulUXwEjsZGAscAQ7MyD/7LE/GVCP29vEQxKc7AAwjT3A2ywHp2xfoCA==", + "dev": true, + "license": "MPL-2.0", + "bin": { + "rlp": "bin/rlp.cjs" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ethereumjs/util": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/util/-/util-9.1.0.tgz", + "integrity": "sha512-XBEKsYqLGXLah9PNJbgdkigthkG7TAGvlD/sH12beMXEyHDyigfcbdvHhmLyDWgDyOJn4QwiQUaF7yeuhnjdog==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "@ethereumjs/rlp": "^5.0.2", + "ethereum-cryptography": "^2.2.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ethereumjs/util/node_modules/@noble/curves": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", + "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.4.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@ethereumjs/util/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@ethereumjs/util/node_modules/ethereum-cryptography": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz", + "integrity": "sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/curves": "1.4.2", + "@noble/hashes": "1.4.0", + "@scure/bip32": "1.4.0", + "@scure/bip39": "1.3.0" + } + }, + "node_modules/@ethersproject/abi": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.8.0.tgz", + "integrity": "sha512-b9YS/43ObplgyV6SlyQsG53/vkSal0MNA1fskSC4mbnCMi8R+NkcH8K9FPYNESf6jUefBUniE4SOKms0E/KK1Q==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "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" + } + }, + "node_modules/@ethersproject/abstract-provider": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.8.0.tgz", + "integrity": "sha512-wC9SFcmh4UK0oKuLJQItoQdzS/qZ51EJegK6EmAWlh+OptpQ/npECOR3QqECd8iGHC0RJb4WKbVdSfif4ammrg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "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" + } + }, + "node_modules/@ethersproject/abstract-signer": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.8.0.tgz", + "integrity": "sha512-N0XhZTswXcmIZQdYtUnd79VJzvEwXQw6PK0dTl9VoYrEBxxCPXqS0Eod7q5TNKRxe1/5WUMuR0u0nqTF/avdCA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "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" + } + }, + "node_modules/@ethersproject/address": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.8.0.tgz", + "integrity": "sha512-GhH/abcC46LJwshoN+uBNoKVFPxUuZm6dA257z0vZkKmU1+t8xTn8oK7B9qrj8W2rFRMch4gbJl6PmVxjxBEBA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "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" + } + }, + "node_modules/@ethersproject/base64": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.8.0.tgz", + "integrity": "sha512-lN0oIwfkYj9LbPx4xEkie6rAMJtySbpOAFXSDVQaBnAzYfB4X2Qr+FXJGxMoc3Bxp2Sm8OwvzMrywxyw0gLjIQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.8.0" + } + }, + "node_modules/@ethersproject/basex": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.8.0.tgz", + "integrity": "sha512-PIgTszMlDRmNwW9nhS6iqtVfdTAKosA7llYXNmGPw4YAI1PUyMv28988wAb41/gHF/WqGdoLv0erHaRcHRKW2Q==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@ethersproject/bytes": "^5.8.0", + "@ethersproject/properties": "^5.8.0" + } + }, + "node_modules/@ethersproject/bignumber": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.8.0.tgz", + "integrity": "sha512-ZyaT24bHaSeJon2tGPKIiHszWjD/54Sz8t57Toch475lCLljC6MgPmxk7Gtzz+ddNN5LuHea9qhAe0x3D+uYPA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.8.0", + "@ethersproject/logger": "^5.8.0", + "bn.js": "^5.2.1" + } + }, + "node_modules/@ethersproject/bytes": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.8.0.tgz", + "integrity": "sha512-vTkeohgJVCPVHu5c25XWaWQOZ4v+DkGoC42/TS2ond+PARCxTJvgTFUNDZovyQ/uAQ4EcpqqowKydcdmRKjg7A==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/logger": "^5.8.0" + } + }, + "node_modules/@ethersproject/constants": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.8.0.tgz", + "integrity": "sha512-wigX4lrf5Vu+axVTIvNsuL6YrV4O5AXl5ubcURKMEME5TnWBouUh0CDTWxZ2GpnRn1kcCgE7l8O5+VbV9QTTcg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bignumber": "^5.8.0" + } + }, + "node_modules/@ethersproject/contracts": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.8.0.tgz", + "integrity": "sha512-0eFjGz9GtuAi6MZwhb4uvUM216F38xiuR0yYCjKJpNfSEy4HUM8hvqqBj9Jmm0IUz8l0xKEhWwLIhPgxNY0yvQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "peer": true, + "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" + } + }, + "node_modules/@ethersproject/hash": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.8.0.tgz", + "integrity": "sha512-ac/lBcTbEWW/VGJij0CNSw/wPcw9bSRgCB0AIBz8CvED/jfvDoV9hsIIiWfvWmFEi8RcXtlNwp2jv6ozWOsooA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "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" + } + }, + "node_modules/@ethersproject/hdnode": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.8.0.tgz", + "integrity": "sha512-4bK1VF6E83/3/Im0ERnnUeWOY3P1BZml4ZD3wcH8Ys0/d1h1xaFt6Zc+Dh9zXf9TapGro0T4wvO71UTCp3/uoA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "peer": true, + "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" + } + }, + "node_modules/@ethersproject/json-wallets": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.8.0.tgz", + "integrity": "sha512-HxblNck8FVUtNxS3VTEYJAcwiKYsBIF77W15HufqlBF9gGfhmYOJtYZp8fSDZtn9y5EaXTE87zDwzxRoTFk11w==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "peer": true, + "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" + } + }, + "node_modules/@ethersproject/json-wallets/node_modules/aes-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", + "integrity": "sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@ethersproject/keccak256": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.8.0.tgz", + "integrity": "sha512-A1pkKLZSz8pDaQ1ftutZoaN46I6+jvuqugx5KYNeQOPqq+JZ0Txm7dlWesCHB5cndJSu5vP2VKptKf7cksERng==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.8.0", + "js-sha3": "0.8.0" + } + }, + "node_modules/@ethersproject/logger": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.8.0.tgz", + "integrity": "sha512-Qe6knGmY+zPPWTC+wQrpitodgBfH7XoceCGL5bJVejmH+yCS3R8jJm8iiWuvWbG76RUmyEG53oqv6GMVWqunjA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT" + }, + "node_modules/@ethersproject/networks": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.8.0.tgz", + "integrity": "sha512-egPJh3aPVAzbHwq8DD7Po53J4OUSsA1MjQp8Vf/OZPav5rlmWUaFLiq8cvQiGK0Z5K6LYzm29+VA/p4RL1FzNg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/logger": "^5.8.0" + } + }, + "node_modules/@ethersproject/pbkdf2": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.8.0.tgz", + "integrity": "sha512-wuHiv97BrzCmfEaPbUFpMjlVg/IDkZThp9Ri88BpjRleg4iePJaj2SW8AIyE8cXn5V1tuAaMj6lzvsGJkGWskg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@ethersproject/bytes": "^5.8.0", + "@ethersproject/sha2": "^5.8.0" + } + }, + "node_modules/@ethersproject/properties": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.8.0.tgz", + "integrity": "sha512-PYuiEoQ+FMaZZNGrStmN7+lWjlsoufGIHdww7454FIaGdbe/p5rnaCXTr5MtBYl3NkeoVhHZuyzChPeGeKIpQw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/logger": "^5.8.0" + } + }, + "node_modules/@ethersproject/providers": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.8.0.tgz", + "integrity": "sha512-3Il3oTzEx3o6kzcg9ZzbE+oCZYyY+3Zh83sKkn4s1DZfTUjIegHnN2Cm0kbn9YFy45FDVcuCLLONhU7ny0SsCw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "peer": true, + "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" + } + }, + "node_modules/@ethersproject/providers/node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "dev": true, + "license": "MIT", + "peer": true, + "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 + } + } + }, + "node_modules/@ethersproject/random": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/random/-/random-5.8.0.tgz", + "integrity": "sha512-E4I5TDl7SVqyg4/kkA/qTfuLWAQGXmSOgYyO01So8hLfwgKvYK5snIlzxJMk72IFdG/7oh8yuSqY2KX7MMwg+A==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@ethersproject/bytes": "^5.8.0", + "@ethersproject/logger": "^5.8.0" + } + }, + "node_modules/@ethersproject/rlp": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.8.0.tgz", + "integrity": "sha512-LqZgAznqDbiEunaUvykH2JAoXTT9NV0Atqk8rQN9nx9SEgThA/WMx5DnW8a9FOufo//6FZOCHZ+XiClzgbqV9Q==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.8.0", + "@ethersproject/logger": "^5.8.0" + } + }, + "node_modules/@ethersproject/sha2": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.8.0.tgz", + "integrity": "sha512-dDOUrXr9wF/YFltgTBYS0tKslPEKr6AekjqDW2dbn1L1xmjGR+9GiKu4ajxovnrDbwxAKdHjW8jNcwfz8PAz4A==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@ethersproject/bytes": "^5.8.0", + "@ethersproject/logger": "^5.8.0", + "hash.js": "1.1.7" + } + }, + "node_modules/@ethersproject/signing-key": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.8.0.tgz", + "integrity": "sha512-LrPW2ZxoigFi6U6aVkFN/fa9Yx/+4AtIUe4/HACTvKJdhm0eeb107EVCIQcrLZkxaSIgc/eCrX8Q1GtbH+9n3w==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.8.0", + "@ethersproject/logger": "^5.8.0", + "@ethersproject/properties": "^5.8.0", + "bn.js": "^5.2.1", + "elliptic": "6.6.1", + "hash.js": "1.1.7" + } + }, + "node_modules/@ethersproject/solidity": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.8.0.tgz", + "integrity": "sha512-4CxFeCgmIWamOHwYN9d+QWGxye9qQLilpgTU0XhYs1OahkclF+ewO+3V1U0mvpiuQxm5EHHmv8f7ClVII8EHsA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "peer": true, + "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" + } + }, + "node_modules/@ethersproject/strings": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.8.0.tgz", + "integrity": "sha512-qWEAk0MAvl0LszjdfnZ2uC8xbR2wdv4cDabyHiBh3Cldq/T8dPH3V4BbBsAYJUeonwD+8afVXld274Ls+Y1xXg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.8.0", + "@ethersproject/constants": "^5.8.0", + "@ethersproject/logger": "^5.8.0" + } + }, + "node_modules/@ethersproject/transactions": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.8.0.tgz", + "integrity": "sha512-UglxSDjByHG0TuU17bDfCemZ3AnKO2vYrL5/2n2oXvKzvb7Cz+W9gOWXKARjp2URVwcWlQlPOEQyAviKwT4AHg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "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" + } + }, + "node_modules/@ethersproject/units": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/units/-/units-5.8.0.tgz", + "integrity": "sha512-lxq0CAnc5kMGIiWW4Mr041VT8IhNM+Pn5T3haO74XZWFulk7wH1Gv64HqE96hT4a7iiNMdOCFEBgaxWuk8ETKQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@ethersproject/bignumber": "^5.8.0", + "@ethersproject/constants": "^5.8.0", + "@ethersproject/logger": "^5.8.0" + } + }, + "node_modules/@ethersproject/wallet": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.8.0.tgz", + "integrity": "sha512-G+jnzmgg6UxurVKRKvw27h0kvG75YKXZKdlLYmAHeF32TGUzHkOFd7Zn6QHOTYRFWnfjtSSFjBowKo7vfrXzPA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "peer": true, + "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" + } + }, + "node_modules/@ethersproject/web": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.8.0.tgz", + "integrity": "sha512-j7+Ksi/9KfGviws6Qtf9Q7KCqRhpwrYKQPs+JBA/rKVFF/yaWLHJEH3zfVP2plVu+eys0d2DlFmhoQJayFewcw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "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" + } + }, + "node_modules/@ethersproject/wordlists": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.8.0.tgz", + "integrity": "sha512-2df9bbXicZws2Sb5S6ET493uJ0Z84Fjr3pC4tu/qlnZERibZCeUVuqdtt+7Tv9xxhUxHoIekIA7avrKUWHrezg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "peer": true, + "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" + } + }, + "node_modules/@fastify/busboy": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", + "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@noble/curves": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", + "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@noble/hashes": "1.3.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/secp256k1": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.1.tgz", + "integrity": "sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT" + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nomicfoundation/edr": { + "version": "0.12.0-next.16", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr/-/edr-0.12.0-next.16.tgz", + "integrity": "sha512-bBL/nHmQwL1WCveALwg01VhJcpVVklJyunG1d/bhJbHgbjzAn6kohVJc7A6gFZegw+Rx38vdxpBkeCDjAEprzw==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@nomicfoundation/edr-darwin-arm64": { + "version": "0.12.0-next.16", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.12.0-next.16.tgz", + "integrity": "sha512-no/8BPVBzVxDGGbDba0zsAxQmVNIq6SLjKzzhCxVKt4tatArXa6+24mr4jXJEmhVBvTNpQsNBO+MMpuEDVaTzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + } + }, + "node_modules/@nomicfoundation/edr-darwin-x64": { + "version": "0.12.0-next.16", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.12.0-next.16.tgz", + "integrity": "sha512-tf36YbcC6po3XYRbi+v0gjwzqg1MvyRqVUujNMXPHgjNWATXNRNOLyjwt2qDn+RD15qtzk70SHVnz9n9mPWzwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + } + }, + "node_modules/@nomicfoundation/edr-linux-arm64-gnu": { + "version": "0.12.0-next.16", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.12.0-next.16.tgz", + "integrity": "sha512-Kr6t9icKSaKtPVbb0TjUcbn3XHqXOGIn+KjKKSSpm6542OkL0HyOi06amh6/8CNke9Gf6Lwion8UJ0aGQhnFwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + } + }, + "node_modules/@nomicfoundation/edr-linux-arm64-musl": { + "version": "0.12.0-next.16", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.12.0-next.16.tgz", + "integrity": "sha512-HaStgfxctSg5PYF+6ooDICL1O59KrgM4XEUsIqoRrjrQax9HnMBXcB8eAj+0O52FWiO9FlchBni2dzh4RjQR2g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + } + }, + "node_modules/@nomicfoundation/edr-linux-x64-gnu": { + "version": "0.12.0-next.16", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.12.0-next.16.tgz", + "integrity": "sha512-8JPTxEZkwOPTgnN4uTWut9ze9R8rp7+T4IfmsKK9i+lDtdbJIxkrFY275YHG2BEYLd7Y5jTa/I4nC74ZpTAvpA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + } + }, + "node_modules/@nomicfoundation/edr-linux-x64-musl": { + "version": "0.12.0-next.16", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.12.0-next.16.tgz", + "integrity": "sha512-KugTrq3iHukbG64DuCYg8uPgiBtrrtX4oZSLba5sjocp0Ul6WWI1FeP1Qule+vClUrHSpJ+wR1G6SE7G0lyS/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + } + }, + "node_modules/@nomicfoundation/edr-win32-x64-msvc": { + "version": "0.12.0-next.16", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.12.0-next.16.tgz", + "integrity": "sha512-Idy0ZjurxElfSmepUKXh6QdptLbW5vUNeIaydvqNogWoTbkJIM6miqZd9lXUy1TYxY7G4Rx5O50c52xc4pFwXQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + } + }, + "node_modules/@nomicfoundation/hardhat-chai-matchers": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-chai-matchers/-/hardhat-chai-matchers-2.1.0.tgz", + "integrity": "sha512-GPhBNafh1fCnVD9Y7BYvoLnblnvfcq3j8YDbO1gGe/1nOFWzGmV7gFu5DkwFXF+IpYsS+t96o9qc/mPu3V3Vfw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/chai-as-promised": "^7.1.3", + "chai-as-promised": "^7.1.1", + "deep-eql": "^4.0.1", + "ordinal": "^1.0.3" + }, + "peerDependencies": { + "@nomicfoundation/hardhat-ethers": "^3.1.0", + "chai": "^4.2.0", + "ethers": "^6.14.0", + "hardhat": "^2.26.0" + } + }, + "node_modules/@nomicfoundation/hardhat-ethers": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-ethers/-/hardhat-ethers-3.1.2.tgz", + "integrity": "sha512-7xEaz2X8p47qWIAqtV2z03MmusheHm8bvY2mDlxo9JiT2BgSx59GSdv5+mzwOvsuKDbTij7oqDnwFyYOlHREEQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "debug": "^4.1.1", + "lodash.isequal": "^4.5.0" + }, + "peerDependencies": { + "ethers": "^6.14.0", + "hardhat": "^2.26.0" + } + }, + "node_modules/@nomicfoundation/hardhat-network-helpers": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-network-helpers/-/hardhat-network-helpers-1.1.2.tgz", + "integrity": "sha512-p7HaUVDbLj7ikFivQVNhnfMHUBgiHYMwQWvGn9AriieuopGOELIrwj2KjyM2a6z70zai5YKO264Vwz+3UFJZPQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ethereumjs-util": "^7.1.4" + }, + "peerDependencies": { + "hardhat": "^2.26.0" + } + }, + "node_modules/@nomicfoundation/hardhat-toolbox": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-toolbox/-/hardhat-toolbox-4.0.0.tgz", + "integrity": "sha512-jhcWHp0aHaL0aDYj8IJl80v4SZXWMS1A2XxXa1CA6pBiFfJKuZinCkO6wb+POAt0LIfXB3gA3AgdcOccrcwBwA==", + "dev": true, + "license": "MIT", + "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" + } + }, + "node_modules/@nomicfoundation/hardhat-verify": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-verify/-/hardhat-verify-2.1.3.tgz", + "integrity": "sha512-danbGjPp2WBhLkJdQy9/ARM3WQIK+7vwzE0urNem1qZJjh9f54Kf5f1xuQv8DvqewUAkuPxVt/7q4Grz5WjqSg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@ethersproject/abi": "^5.1.2", + "@ethersproject/address": "^5.0.2", + "cbor": "^8.1.0", + "debug": "^4.1.1", + "lodash.clonedeep": "^4.5.0", + "picocolors": "^1.1.0", + "semver": "^6.3.0", + "table": "^6.8.0", + "undici": "^5.14.0" + }, + "peerDependencies": { + "hardhat": "^2.26.0" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer/-/solidity-analyzer-0.1.2.tgz", + "integrity": "sha512-q4n32/FNKIhQ3zQGGw5CvPF6GTvDCpYwIf7bEY/dZTZbgfDsHyjJwURxUJf3VQuuJj+fDIFl4+KkBVbw4Ef6jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + }, + "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" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-darwin-arm64": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-darwin-arm64/-/solidity-analyzer-darwin-arm64-0.1.2.tgz", + "integrity": "sha512-JaqcWPDZENCvm++lFFGjrDd8mxtf+CtLd2MiXvMNTBD33dContTZ9TWETwNFwg7JTJT5Q9HEecH7FA+HTSsIUw==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-darwin-x64": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-darwin-x64/-/solidity-analyzer-darwin-x64-0.1.2.tgz", + "integrity": "sha512-fZNmVztrSXC03e9RONBT+CiksSeYcxI1wlzqyr0L7hsQlK1fzV+f04g2JtQ1c/Fe74ZwdV6aQBdd6Uwl1052sw==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-linux-arm64-gnu": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-arm64-gnu/-/solidity-analyzer-linux-arm64-gnu-0.1.2.tgz", + "integrity": "sha512-3d54oc+9ZVBuB6nbp8wHylk4xh0N0Gc+bk+/uJae+rUgbOBwQSfuGIbAZt1wBXs5REkSmynEGcqx6DutoK0tPA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-linux-arm64-musl": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-arm64-musl/-/solidity-analyzer-linux-arm64-musl-0.1.2.tgz", + "integrity": "sha512-iDJfR2qf55vgsg7BtJa7iPiFAsYf2d0Tv/0B+vhtnI16+wfQeTbP7teookbGvAo0eJo7aLLm0xfS/GTkvHIucA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-linux-x64-gnu": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-x64-gnu/-/solidity-analyzer-linux-x64-gnu-0.1.2.tgz", + "integrity": "sha512-9dlHMAt5/2cpWyuJ9fQNOUXFB/vgSFORg1jpjX1Mh9hJ/MfZXlDdHQ+DpFCs32Zk5pxRBb07yGvSHk9/fezL+g==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-linux-x64-musl": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-x64-musl/-/solidity-analyzer-linux-x64-musl-0.1.2.tgz", + "integrity": "sha512-GzzVeeJob3lfrSlDKQw2bRJ8rBf6mEYaWY+gW0JnTDHINA0s2gPR4km5RLIj1xeZZOYz4zRw+AEeYgLRqB2NXg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-win32-x64-msvc": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-win32-x64-msvc/-/solidity-analyzer-win32-x64-msvc-0.1.2.tgz", + "integrity": "sha512-Fdjli4DCcFHb4Zgsz0uEJXZ2K7VEO+w5KVv7HmT7WO10iODdU9csC2az4jrhEsRtiR9Gfd74FlG0NYlw1BMdyA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@openzeppelin/contracts": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-5.4.0.tgz", + "integrity": "sha512-eCYgWnLg6WO+X52I16TZt8uEjbtdkgLC0SUX/xnAksjjrQI4Xfn4iBRoI5j55dmlOhDv1Y7BoR3cU7e3WWhC6A==", + "license": "MIT" + }, + "node_modules/@scure/base": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz", + "integrity": "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.4.0.tgz", + "integrity": "sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/curves": "~1.4.0", + "@noble/hashes": "~1.4.0", + "@scure/base": "~1.1.6" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32/node_modules/@noble/curves": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", + "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.4.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32/node_modules/@scure/base": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", + "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.3.0.tgz", + "integrity": "sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.4.0", + "@scure/base": "~1.1.6" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39/node_modules/@scure/base": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", + "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@sentry/core": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.30.0.tgz", + "integrity": "sha512-TmfrII8w1PQZSZgPpUESqjB+jC6MvZJZdLtE/0hZ+SrnKhW3x5WlYLvTXZpcWePYBku7rl2wn1RZu6uT0qCTeg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sentry/hub": "5.30.0", + "@sentry/minimal": "5.30.0", + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/core/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@sentry/hub": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.30.0.tgz", + "integrity": "sha512-2tYrGnzb1gKz2EkMDQcfLrDTvmGcQPuWxLnJKXJvYTQDGLlEvi2tWz1VIHjunmOvJrB5aIQLhm+dcMRwFZDCqQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/hub/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@sentry/minimal": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.30.0.tgz", + "integrity": "sha512-BwWb/owZKtkDX+Sc4zCSTNcvZUq7YcH3uAVlmh/gtR9rmUvbzAA3ewLuB3myi4wWRAMEtny6+J/FN/x+2wn9Xw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sentry/hub": "5.30.0", + "@sentry/types": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/minimal/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@sentry/node": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-5.30.0.tgz", + "integrity": "sha512-Br5oyVBF0fZo6ZS9bxbJZG4ApAjRqAnqFFurMVJJdunNb80brh7a5Qva2kjhm+U6r9NJAB5OmDyPkA1Qnt+QVg==", + "dev": true, + "license": "BSD-3-Clause", + "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.1", + "https-proxy-agent": "^5.0.0", + "lru_map": "^0.3.3", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/node/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@sentry/tracing": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-5.30.0.tgz", + "integrity": "sha512-dUFowCr0AIMwiLD7Fs314Mdzcug+gBVo/+NCMyDw8tFxJkwWAKl7Qa2OZxLQ0ZHjakcj1hNKfCQJ9rhyfOl4Aw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sentry/hub": "5.30.0", + "@sentry/minimal": "5.30.0", + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/tracing/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@sentry/types": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.30.0.tgz", + "integrity": "sha512-R8xOqlSTZ+htqrfteCWU5Nk0CDN5ApUTvrlvBuiH1DyP6czDZ4ktbZB0hAgBlVcK0U+qpD3ag3Tqqpa5Q67rPw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/utils": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.30.0.tgz", + "integrity": "sha512-zaYmoH0NWWtvnJjC9/CBseXMtKHm/tm40sz3YfJRxeQjyzRqNQPgivpd9R/oDJCYj999mzdW382p/qi2ypjLww==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sentry/types": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/utils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@solidity-parser/parser": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.14.5.tgz", + "integrity": "sha512-6dKnHZn7fg/iQATVEzqyUOyEidbn05q7YA2mQ9hC0MMXhhV3/JrsxmFSYZAcr7j1yUP700LLhTruvJ3MiQmjJg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "antlr4ts": "^0.5.0-alpha.4" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", + "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@typechain/ethers-v6": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@typechain/ethers-v6/-/ethers-v6-0.5.1.tgz", + "integrity": "sha512-F+GklO8jBWlsaVV+9oHaPh5NJdd6rAKN4tklGfInX1Q7h0xPgVLP39Jl3eCulPB5qexI71ZFHwbljx4ZXNfouA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "lodash": "^4.17.15", + "ts-essentials": "^7.0.1" + }, + "peerDependencies": { + "ethers": "6.x", + "typechain": "^8.3.2", + "typescript": ">=4.7.0" + } + }, + "node_modules/@typechain/hardhat": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/@typechain/hardhat/-/hardhat-9.1.0.tgz", + "integrity": "sha512-mtaUlzLlkqTlfPwB3FORdejqBskSnh+Jl8AIJGjXNAQfRQ4ofHADPl1+oU7Z3pAJzmZbUXII8MhOLQltcHgKnA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "fs-extra": "^9.1.0" + }, + "peerDependencies": { + "@typechain/ethers-v6": "^0.5.1", + "ethers": "^6.1.0", + "hardhat": "^2.9.9", + "typechain": "^8.3.2" + } + }, + "node_modules/@types/bn.js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.2.0.tgz", + "integrity": "sha512-DLbJ1BPqxvQhIGbeu8VbUC1DiAiahHtAYvA0ZEAa4P31F7IaArc8z3C3BRQdWX4mtLQuABG4yzp76ZrS02Ui1Q==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/chai": { + "version": "4.3.20", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.20.tgz", + "integrity": "sha512-/pC9HAB5I/xMlc5FP77qjCnI16ChlJfW0tGa0IUcFn38VJrTV6DeZ60NU5KZBtaOZqjdpwTWohz5HU1RrhiYxQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/chai-as-promised": { + "version": "7.1.8", + "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-7.1.8.tgz", + "integrity": "sha512-ThlRVIJhr69FLlh6IctTXFkmhtP3NpMZ2QGq69StYLyKZFp/HOp1VdKZj7RvfNWYYcJ1xlbLGLLWj1UvP5u/Gw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/chai": "*" + } + }, + "node_modules/@types/concat-stream": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@types/concat-stream/-/concat-stream-1.6.1.tgz", + "integrity": "sha512-eHE4cQPoj6ngxBZMvVf6Hw7Mh4jMW4U9lpGmS5GBPB9RYxlFg+CHaVN7ErNY4W9XfLIEn20b4VDYaIrbq0q4uA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/form-data": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-0.0.33.tgz", + "integrity": "sha512-8BSvG1kGm83cyJITQMZSulnl6QV8jqAGreJsc5tPu1Jq0vTSOiY/k24Wx82JRpWwZSqrala6sd5rWi6aNXvqcw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "node_modules/@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/mocha": { + "version": "10.0.10", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", + "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/node": { + "version": "24.10.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", + "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-uRwJqmiXmh9++aSu1VNEn3iIxWOhd8AHXNSdlaLfdAAdSTY9jYVeGWnzejM3dvrkbqE3/hyQkQQ29IFATEGlew==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/prettier": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", + "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/secp256k1": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.7.tgz", + "integrity": "sha512-Rcvjl6vARGAKRO6jHeKMatGrvOMGrR/AR11N1x2LqintPCyDZ7NBhrh238Z2VZc7aM7KIwnFpFQ7fnfK4H/9Qw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/abbrev": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", + "integrity": "sha512-LEyx4aLEC3x6T0UguF6YILf+ntvmOaWsVfENmIW0E9H09vKlLDGelMjjSm0jkDHALj8A8quZ/HapKNigzwge+Q==", + "dev": true, + "license": "ISC", + "peer": true + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/adm-zip": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz", + "integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.3.0" + } + }, + "node_modules/aes-js": { + "version": "4.0.0-beta.5", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz", + "integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==", + "dev": true, + "license": "BSD-3-Clause OR MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=0.4.2" + } + }, + "node_modules/ansi-align": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.1.0" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/antlr4ts": { + "version": "0.5.0-alpha.4", + "resolved": "https://registry.npmjs.org/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz", + "integrity": "sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/array-back": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", + "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "license": "ISC", + "peer": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axios": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", + "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/base-x": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.11.tgz", + "integrity": "sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/bech32": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", + "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/blakejs": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz", + "integrity": "sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/bn.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.2.tgz", + "integrity": "sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/boxen": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", + "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-align": "^3.0.0", + "camelcase": "^6.2.0", + "chalk": "^4.1.0", + "cli-boxes": "^2.2.1", + "string-width": "^4.2.2", + "type-fest": "^0.20.2", + "widest-line": "^3.1.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", + "dev": true, + "license": "MIT" + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true, + "license": "ISC" + }, + "node_modules/browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "base-x": "^3.0.2" + } + }, + "node_modules/bs58check": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", + "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "bs58": "^4.0.0", + "create-hash": "^1.1.0", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", + "dev": true, + "license": "Apache-2.0", + "peer": true + }, + "node_modules/cbor": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/cbor/-/cbor-8.1.0.tgz", + "integrity": "sha512-DwGjNW9omn6EwP70aXsn7FQJx5kO12tX0bZkaTjzdVFM6/7nhA4t0EENocKGx6D2Bch9PE2KzCUf5SceBdeijg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "nofilter": "^3.1.0" + }, + "engines": { + "node": ">=12.19" + } + }, + "node_modules/chai": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", + "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chai-as-promised": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.2.tgz", + "integrity": "sha512-aBDHZxRzYnUYuIAIPBH2s511DjlKPzXNlXSGFC8CwmroWQLfrW0LtE1nK3MAwwNhJPa9raEjNCmRoFpG0Hurdw==", + "dev": true, + "license": "WTFPL", + "peer": true, + "dependencies": { + "check-error": "^1.0.2" + }, + "peerDependencies": { + "chai": ">= 2.1.2 < 6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cipher-base": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.7.tgz", + "integrity": "sha512-Mz9QMT5fJe7bKI7MH31UilT5cEK5EHHRCccw/YRFsRY47AuNgaV6HY3rscp0/I4Q+tTW/5zoqpSeRRI54TkDWA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1", + "to-buffer": "^1.2.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-boxes": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", + "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-table3": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz", + "integrity": "sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "object-assign": "^4.1.0", + "string-width": "^2.1.1" + }, + "engines": { + "node": ">=6" + }, + "optionalDependencies": { + "colors": "^1.1.2" + } + }, + "node_modules/cli-table3/node_modules/ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/cli-table3/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/cli-table3/node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cli-table3/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/command-exists": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", + "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==", + "dev": true, + "license": "MIT" + }, + "node_modules/command-line-args": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.2.1.tgz", + "integrity": "sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "array-back": "^3.1.0", + "find-replace": "^3.0.0", + "lodash.camelcase": "^4.3.0", + "typical": "^4.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/command-line-usage": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-6.1.3.tgz", + "integrity": "sha512-sH5ZSPr+7UStsloltmDh7Ce5fb8XPlHyoPzTpyyMuYCtervL65+ubVZ6Q61cFtFl62UyJlc8/JwERRbAFPUqgw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "array-back": "^4.0.2", + "chalk": "^2.4.2", + "table-layout": "^1.0.2", + "typical": "^5.2.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/command-line-usage/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/command-line-usage/node_modules/array-back": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz", + "integrity": "sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/command-line-usage/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/command-line-usage/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/command-line-usage/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/command-line-usage/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/command-line-usage/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/command-line-usage/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/command-line-usage/node_modules/typical": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz", + "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "engines": [ + "node >= 0.8" + ], + "license": "MIT", + "peer": true, + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/concat-stream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/concat-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/concat-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/death": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/death/-/death-1.1.0.tgz", + "integrity": "sha512-vsV6S4KVHvTGxbEcij7hkWRv0It+sGGWVOM67dQde/o5Xjnr+KmLjxWJii2uEObIrt1CcM9w0Yaovx+iOlIL+w==", + "dev": true, + "peer": true + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-eql": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", + "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/diff": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/difflib": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/difflib/-/difflib-0.2.4.tgz", + "integrity": "sha512-9YVwmMb0wQHQNr5J9m6BSj6fk4pfGITGQOOs+D9Fl+INODWFOfvhIU1hNv6GgR1RBoC/9NJcwu77zShxV0kT7w==", + "dev": true, + "peer": true, + "dependencies": { + "heap": ">= 0.2.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dotenv": { + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", + "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/elliptic": { + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.1.tgz", + "integrity": "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/enquirer": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.1", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/escodegen": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", + "integrity": "sha512-yhi5S+mNTOuRvyW4gWlg5W1byMaQGWWSYHXsuFZ7GBo7tpyOwi2EdzMP/QWxh9hwkD2m+wDVHJsxhRIj+v/b/A==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "dependencies": { + "esprima": "^2.7.1", + "estraverse": "^1.9.1", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=0.12.0" + }, + "optionalDependencies": { + "source-map": "~0.2.0" + } + }, + "node_modules/esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha512-OarPfz0lFCiW4/AV2Oy1Rp9qu0iusTKqykwTspGCZtPxmF81JR4MmIebvF1F9+UOKth2ZubLQ4XGGaU+hSn99A==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/estraverse": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", + "integrity": "sha512-25w1fMXQrGdoquWnScXZGckOv+Wes+JDnuN/+7ex3SauFRS72r2lFDec0EKPt2YD1wUJ/IrfEex+9yp4hfSOJA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eth-gas-reporter": { + "version": "0.2.27", + "resolved": "https://registry.npmjs.org/eth-gas-reporter/-/eth-gas-reporter-0.2.27.tgz", + "integrity": "sha512-femhvoAM7wL0GcI8ozTdxfuBtBFJ9qsyIAsmKVjlWAHUbdnnXHt+lKzz/kmldM5lA9jLuNHGwuIxorNpLbR1Zw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@solidity-parser/parser": "^0.14.0", + "axios": "^1.5.1", + "cli-table3": "^0.5.0", + "colors": "1.4.0", + "ethereum-cryptography": "^1.0.3", + "ethers": "^5.7.2", + "fs-readdir-recursive": "^1.1.0", + "lodash": "^4.17.14", + "markdown-table": "^1.1.3", + "mocha": "^10.2.0", + "req-cwd": "^2.0.0", + "sha1": "^1.1.1", + "sync-request": "^6.0.0" + }, + "peerDependencies": { + "@codechecks/client": "^0.1.0" + }, + "peerDependenciesMeta": { + "@codechecks/client": { + "optional": true + } + } + }, + "node_modules/eth-gas-reporter/node_modules/@noble/hashes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.2.0.tgz", + "integrity": "sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT", + "peer": true + }, + "node_modules/eth-gas-reporter/node_modules/@scure/base": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", + "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==", + "dev": true, + "license": "MIT", + "peer": true, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/eth-gas-reporter/node_modules/@scure/bip32": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.1.5.tgz", + "integrity": "sha512-XyNh1rB0SkEqd3tXcXMi+Xe1fvg+kUIcoRIEujP1Jgv7DqW2r9lg3Ah0NkFaCs9sTkQAQA8kw7xiRXzENi9Rtw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@noble/hashes": "~1.2.0", + "@noble/secp256k1": "~1.7.0", + "@scure/base": "~1.1.0" + } + }, + "node_modules/eth-gas-reporter/node_modules/@scure/bip39": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.1.1.tgz", + "integrity": "sha512-t+wDck2rVkh65Hmv280fYdVdY25J9YeEUIgn2LG1WM6gxFkGzcksoDiUkWVpVp3Oex9xGC68JU2dSbUfwZ2jPg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@noble/hashes": "~1.2.0", + "@scure/base": "~1.1.0" + } + }, + "node_modules/eth-gas-reporter/node_modules/ethereum-cryptography": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-1.2.0.tgz", + "integrity": "sha512-6yFQC9b5ug6/17CQpCyE3k9eKBMdhyVjzUy1WkiuY/E4vj/SXDBbCw8QEIaXqf0Mf2SnY6RmpDcwlUmBSS0EJw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@noble/hashes": "1.2.0", + "@noble/secp256k1": "1.7.1", + "@scure/bip32": "1.1.5", + "@scure/bip39": "1.1.1" + } + }, + "node_modules/eth-gas-reporter/node_modules/ethers": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.8.0.tgz", + "integrity": "sha512-DUq+7fHrCg1aPDFCHx6UIPb3nmt2XMpM7Y/g2gLhsl3lIBqeAfOJIl1qEvRf2uq3BiKxmh6Fh5pfp2ieyek7Kg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "peer": true, + "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" + } + }, + "node_modules/ethereum-bloom-filters": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ethereum-bloom-filters/-/ethereum-bloom-filters-1.2.0.tgz", + "integrity": "sha512-28hyiE7HVsWubqhpVLVmZXFd4ITeHi+BUu05o9isf0GUpMtzBUi+8/gFrGaGYzvGAJQmJ3JKj77Mk9G98T84rA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@noble/hashes": "^1.4.0" + } + }, + "node_modules/ethereum-bloom-filters/node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ethereum-cryptography": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", + "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/pbkdf2": "^3.0.0", + "@types/secp256k1": "^4.0.1", + "blakejs": "^1.1.0", + "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.0", + "pbkdf2": "^3.0.17", + "randombytes": "^2.1.0", + "safe-buffer": "^5.1.2", + "scrypt-js": "^3.0.0", + "secp256k1": "^4.0.1", + "setimmediate": "^1.0.5" + } + }, + "node_modules/ethereumjs-util": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz", + "integrity": "sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg==", + "dev": true, + "license": "MPL-2.0", + "peer": true, + "dependencies": { + "@types/bn.js": "^5.1.0", + "bn.js": "^5.1.2", + "create-hash": "^1.1.2", + "ethereum-cryptography": "^0.1.3", + "rlp": "^2.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/ethers": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.15.0.tgz", + "integrity": "sha512-Kf/3ZW54L4UT0pZtsY/rf+EkBU7Qi5nnhonjUb8yTXcxH3cdcWrV2cRyk0Xk/4jK6OoHhxxZHriyhje20If2hQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/ethers-io/" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "peer": true, + "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" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/ethers/node_modules/@types/node": { + "version": "22.7.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz", + "integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/ethers/node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/ethjs-unit": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ethjs-unit/-/ethjs-unit-0.1.6.tgz", + "integrity": "sha512-/Sn9Y0oKl0uqQuvgFk/zQgR7aw1g36qX/jzSQ5lSwlO0GigPymk4eGQfeNTD03w1dPOqfz8V77Cy43jH56pagw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "bn.js": "4.11.6", + "number-to-bn": "1.7.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/ethjs-unit/node_modules/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-replace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz", + "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "array-back": "^3.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fp-ts": { + "version": "1.19.3", + "resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-1.19.3.tgz", + "integrity": "sha512-H5KQDspykdHuztLTg+ajGN0Z2qUjcEf3Ybxc6hLt0k7/zPkn29XnKnxlBPyW2XIddWrGaJBzBl4VLYOtk39yZg==", + "dev": true, + "license": "MIT" + }, + "node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fs-readdir-recursive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", + "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "peer": true, + "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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-port": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz", + "integrity": "sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ghost-testrpc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/ghost-testrpc/-/ghost-testrpc-0.0.2.tgz", + "integrity": "sha512-i08dAEgJ2g8z5buJIrCTduwPIhih3DP+hOCTyyryikfV8T0bNvHnGXO67i0DD1H4GBDETTclPy9njZbfluQYrQ==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "chalk": "^2.4.2", + "node-emoji": "^1.10.0" + }, + "bin": { + "testrpc-sc": "index.js" + } + }, + "node_modules/ghost-testrpc/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ghost-testrpc/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ghost-testrpc/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/ghost-testrpc/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/ghost-testrpc/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/ghost-testrpc/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/ghost-testrpc/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "global-prefix": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/globby": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.2.tgz", + "integrity": "sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/glob": "^7.1.1", + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.0.3", + "glob": "^7.1.3", + "ignore": "^5.1.1", + "merge2": "^1.2.3", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/globby/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/globby/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globby/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/handlebars/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hardhat": { + "version": "2.27.0", + "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-2.27.0.tgz", + "integrity": "sha512-du7ecjx1/ueAUjvtZhVkJvWytPCjlagG3ZktYTphfzAbc1Flc6sRolw5mhKL/Loub1EIFRaflutM4bdB/YsUUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ethereumjs/util": "^9.1.0", + "@ethersproject/abi": "^5.1.2", + "@nomicfoundation/edr": "^0.12.0-next.7", + "@nomicfoundation/solidity-analyzer": "^0.1.0", + "@sentry/node": "^5.18.1", + "adm-zip": "^0.4.16", + "aggregate-error": "^3.0.0", + "ansi-escapes": "^4.3.0", + "boxen": "^5.1.2", + "chokidar": "^4.0.0", + "ci-info": "^2.0.0", + "debug": "^4.1.1", + "enquirer": "^2.3.0", + "env-paths": "^2.2.0", + "ethereum-cryptography": "^1.0.3", + "find-up": "^5.0.0", + "fp-ts": "1.19.3", + "fs-extra": "^7.0.1", + "immutable": "^4.0.0-rc.12", + "io-ts": "1.10.4", + "json-stream-stringify": "^3.1.4", + "keccak": "^3.0.2", + "lodash": "^4.17.11", + "micro-eth-signer": "^0.14.0", + "mnemonist": "^0.38.0", + "mocha": "^10.0.0", + "p-map": "^4.0.0", + "picocolors": "^1.1.0", + "raw-body": "^2.4.1", + "resolve": "1.17.0", + "semver": "^6.3.0", + "solc": "0.8.26", + "source-map-support": "^0.5.13", + "stacktrace-parser": "^0.1.10", + "tinyglobby": "^0.2.6", + "tsort": "0.0.1", + "undici": "^5.14.0", + "uuid": "^8.3.2", + "ws": "^7.4.6" + }, + "bin": { + "hardhat": "internal/cli/bootstrap.js" + }, + "peerDependencies": { + "ts-node": "*", + "typescript": "*" + }, + "peerDependenciesMeta": { + "ts-node": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/hardhat-gas-reporter": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/hardhat-gas-reporter/-/hardhat-gas-reporter-1.0.10.tgz", + "integrity": "sha512-02N4+So/fZrzJ88ci54GqwVA3Zrf0C9duuTyGt0CFRIh/CdNwbnTgkXkRfojOMLBQ+6t+lBIkgbsOtqMvNwikA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "array-uniq": "1.0.3", + "eth-gas-reporter": "^0.2.25", + "sha1": "^1.1.1" + }, + "peerDependencies": { + "hardhat": "^2.0.2" + } + }, + "node_modules/hardhat/node_modules/@noble/hashes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.2.0.tgz", + "integrity": "sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT" + }, + "node_modules/hardhat/node_modules/@scure/base": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", + "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/hardhat/node_modules/@scure/bip32": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.1.5.tgz", + "integrity": "sha512-XyNh1rB0SkEqd3tXcXMi+Xe1fvg+kUIcoRIEujP1Jgv7DqW2r9lg3Ah0NkFaCs9sTkQAQA8kw7xiRXzENi9Rtw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.2.0", + "@noble/secp256k1": "~1.7.0", + "@scure/base": "~1.1.0" + } + }, + "node_modules/hardhat/node_modules/@scure/bip39": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.1.1.tgz", + "integrity": "sha512-t+wDck2rVkh65Hmv280fYdVdY25J9YeEUIgn2LG1WM6gxFkGzcksoDiUkWVpVp3Oex9xGC68JU2dSbUfwZ2jPg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.2.0", + "@scure/base": "~1.1.0" + } + }, + "node_modules/hardhat/node_modules/ethereum-cryptography": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-1.2.0.tgz", + "integrity": "sha512-6yFQC9b5ug6/17CQpCyE3k9eKBMdhyVjzUy1WkiuY/E4vj/SXDBbCw8QEIaXqf0Mf2SnY6RmpDcwlUmBSS0EJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.2.0", + "@noble/secp256k1": "1.7.1", + "@scure/bip32": "1.1.5", + "@scure/bip39": "1.1.1" + } + }, + "node_modules/hardhat/node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/hardhat/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/hardhat/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/hardhat/node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "dev": true, + "license": "MIT", + "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 + } + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hash-base": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.2.tgz", + "integrity": "sha512-Bb33KbowVTIj5s7Ked1OsqHUeCpz//tPwR+E2zJgJKo9Z5XolZ9b6bdUgjmYlwnWhoOQKoTd1TYToZGn5mAYOg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^2.3.8", + "safe-buffer": "^5.2.1", + "to-buffer": "^1.2.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/hash-base/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/hash-base/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/hash-base/node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/hash-base/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/hash-base/node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/heap": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.7.tgz", + "integrity": "sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/http-basic": { + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/http-basic/-/http-basic-8.1.3.tgz", + "integrity": "sha512-/EcDMwJZh3mABI2NhGfHOGOeOZITqfkEO4p/xK+l3NpyncIHUQBoMvCSF/b5GqvKtySC2srL/GGG3+EtlqlmCw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "caseless": "^0.12.0", + "concat-stream": "^1.6.2", + "http-response-object": "^3.0.1", + "parse-cache-control": "^1.0.1" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-response-object": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/http-response-object/-/http-response-object-3.0.2.tgz", + "integrity": "sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "^10.0.3" + } + }, + "node_modules/http-response-object/node_modules/@types/node": { + "version": "10.17.60", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", + "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/immutable": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", + "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==", + "dev": true, + "license": "MIT" + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "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.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC", + "peer": true + }, + "node_modules/interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/io-ts": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/io-ts/-/io-ts-1.10.4.tgz", + "integrity": "sha512-b23PteSnYXSONJ6JQXRAlvJhuw8KOtkqa87W4wDtvMrud/DTJd5X+NpOOI+O/zZwVq6v0VLAaJ+1EDViKEuN9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fp-ts": "^1.0.0" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-hex-prefixed": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz", + "integrity": "sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC", + "peer": true + }, + "node_modules/js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/json-stream-stringify": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/json-stream-stringify/-/json-stream-stringify-3.1.6.tgz", + "integrity": "sha512-x7fpwxOkbhFCaJDJ8vb1fBY3DdSa4AlITaz+HHILQJzdPMnHEFjxPwVUi1ALIbcIxDE0PNe/0i7frnY8QnBQog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=7.10.1" + } + }, + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonschema": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.5.0.tgz", + "integrity": "sha512-K+A9hhqbn0f3pJX17Q/7H6yQfD/5OXgdrR5UE12gMXCiN9D5Xq2o5mddV2QEcX/bjla99ASsAAQUyMCCRWAEhw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/keccak": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.4.tgz", + "integrity": "sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "get-func-name": "^2.0.1" + } + }, + "node_modules/lru_map": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz", + "integrity": "sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC", + "peer": true + }, + "node_modules/markdown-table": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-1.1.3.tgz", + "integrity": "sha512-1RUZVgQlpJSPWYbFSpmudq5nHY1doEIv89gBtF0s4gW1GF2XorxcA/70M5vq7rLv0a6mhOUccRsqkwhwLCIQ2Q==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micro-eth-signer": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/micro-eth-signer/-/micro-eth-signer-0.14.0.tgz", + "integrity": "sha512-5PLLzHiVYPWClEvZIXXFu5yutzpadb73rnQCpUqIHu3No3coFuWQNfE5tkBQJ7djuLYl6aRLaS0MgWJYGoqiBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/curves": "~1.8.1", + "@noble/hashes": "~1.7.1", + "micro-packed": "~0.7.2" + } + }, + "node_modules/micro-eth-signer/node_modules/@noble/curves": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.8.2.tgz", + "integrity": "sha512-vnI7V6lFNe0tLAuJMu+2sX+FcL14TaCWy1qiczg1VwRmPrpQCdq5ESXQMqUc2tluRNf6irBXrWbl1mGN8uaU/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.7.2" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/micro-eth-signer/node_modules/@noble/hashes": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.2.tgz", + "integrity": "sha512-biZ0NUSxyjLLqo6KxEJ1b+C2NAx0wtDoFvCaXHGgUkeHzf3Xc1xKumFKREuT7f7DARNZ/slvYUwFG6B0f2b6hQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/micro-ftch": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/micro-ftch/-/micro-ftch-0.3.1.tgz", + "integrity": "sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/micro-packed": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/micro-packed/-/micro-packed-0.7.3.tgz", + "integrity": "sha512-2Milxs+WNC00TRlem41oRswvw31146GiSaoCT7s3Xi2gMUglW5QBeqlQaZeHr5tJx9nm3i57LNXPqxOOaWtTYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@scure/base": "~1.2.5" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true, + "license": "ISC" + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", + "dev": true, + "license": "MIT" + }, + "node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mnemonist": { + "version": "0.38.5", + "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.38.5.tgz", + "integrity": "sha512-bZTFT5rrPKtPJxj8KSV0WkPyNxl72vQepqqVUAW2ARUpUSF2qXMB6jZj7hW5/k7C1rtpzqbD/IIbJwLXUjCHeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "obliterator": "^2.0.0" + } + }, + "node_modules/mocha": { + "version": "10.8.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz", + "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "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.0", + "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" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/mocha/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/mocha/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/node-addon-api": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", + "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-emoji": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", + "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "lodash": "^4.17.21" + } + }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "dev": true, + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/nofilter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/nofilter/-/nofilter-3.1.0.tgz", + "integrity": "sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12.19" + } + }, + "node_modules/nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha512-4GUt3kSEYmk4ITxzB/b9vaIDfUVWN/Ml1Fwl11IlnIG2iaJ9O6WXZ9SrYM9NLI8OCBieN2Y8SWC2oJV0RQ7qYg==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/number-to-bn": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/number-to-bn/-/number-to-bn-1.7.0.tgz", + "integrity": "sha512-wsJ9gfSz1/s4ZsJN01lyonwuxA1tml6X1yBDnfpMglypcBRFZZkus26EdPSlqS5GJfYddVZa22p3VNb3z5m5Ig==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "bn.js": "4.11.6", + "strip-hex-prefix": "1.0.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/number-to-bn/node_modules/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obliterator": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.5.tgz", + "integrity": "sha512-42CPE9AhahZRsMNslczq0ctAEtqk8Eka26QofnqC346BZdHDySk3LWka23LI7ULIw11NmltpiLagIq8gBozxTw==", + "dev": true, + "license": "MIT" + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ordinal": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ordinal/-/ordinal-1.0.3.tgz", + "integrity": "sha512-cMddMgb2QElm8G7vdaa02jhUNbTSrhsgAGUz1OokD83uJTwSUn+nKoNoKVVaRa08yF6sgfO7Maou1+bgLd9rdQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-cache-control": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-cache-control/-/parse-cache-control-1.0.1.tgz", + "integrity": "sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==", + "dev": true, + "peer": true + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/pbkdf2": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.5.tgz", + "integrity": "sha512-Q3CG/cYvCO1ye4QKkuH7EXxs3VC/rI1/trd+qX2+PolbaKG0H+bgcZzrTt96mMyRtejk+JMCiLUn3y29W8qmFQ==", + "dev": true, + "license": "MIT", + "peer": true, + "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.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/promise": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", + "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "asap": "~2.0.6" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "peer": true + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", + "dev": true, + "peer": true, + "dependencies": { + "resolve": "^1.1.6" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/recursive-readdir": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", + "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/recursive-readdir/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/recursive-readdir/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/reduce-flatten": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-2.0.0.tgz", + "integrity": "sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/req-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/req-cwd/-/req-cwd-2.0.0.tgz", + "integrity": "sha512-ueoIoLo1OfB6b05COxAA9UpeoscNpYyM+BqYlA7H6LVF4hKGPXQQSSaD2YmvDVJMkk4UDpAHIeU1zG53IqjvlQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "req-from": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/req-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/req-from/-/req-from-2.0.0.tgz", + "integrity": "sha512-LzTfEVDVQHBRfjOUMgNBA+V6DWsSnoeKzf42J7l0xa/B4jyPOuuF5MlNSmomLNGemWTnV2TIdjSSLnEn95fOQA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "resolve-from": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/ripemd160": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.3.tgz", + "integrity": "sha512-5Di9UC0+8h1L6ZD2d7awM7E/T4uA1fJRlx6zk/NvdCCVEoAnFqvHmCuNeIKoCeIixBX/q8uM+6ycDvF8woqosA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "hash-base": "^3.1.2", + "inherits": "^2.0.4" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/rlp": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.7.tgz", + "integrity": "sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ==", + "dev": true, + "license": "MPL-2.0", + "peer": true, + "dependencies": { + "bn.js": "^5.2.0" + }, + "bin": { + "rlp": "bin/rlp" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/sc-istanbul": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/sc-istanbul/-/sc-istanbul-0.4.6.tgz", + "integrity": "sha512-qJFF/8tW/zJsbyfh/iT/ZM5QNHE3CXxtLJbZsL+CzdJLBsPD7SedJZoUA4d8iAcN2IoMp/Dx80shOOd2x96X/g==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "abbrev": "1.0.x", + "async": "1.x", + "escodegen": "1.8.x", + "esprima": "2.7.x", + "glob": "^5.0.15", + "handlebars": "^4.0.1", + "js-yaml": "3.x", + "mkdirp": "0.5.x", + "nopt": "3.x", + "once": "1.x", + "resolve": "1.1.x", + "supports-color": "^3.1.0", + "which": "^1.1.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "istanbul": "lib/cli.js" + } + }, + "node_modules/sc-istanbul/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/sc-istanbul/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/sc-istanbul/node_modules/glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha512-c9IPMazfRITpmAAKi22dK1VKxGDX9ehhqfABDriL/lzO92xcUKEJPQHrVA/2YHSNFB4iFlykVmWvwo48nr3OxA==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/sc-istanbul/node_modules/has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sc-istanbul/node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/sc-istanbul/node_modules/js-yaml/node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/sc-istanbul/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/sc-istanbul/node_modules/resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/sc-istanbul/node_modules/supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/scrypt-js": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", + "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/secp256k1": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.4.tgz", + "integrity": "sha512-6JfvwvjUOn8F/jUoBY2Q1v5WY5XS+rj8qSe0v8Y4ezH4InLgTEeOOPQsRll9OV429Pvo6BCHGavIyJfr3TAhsw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "peer": true, + "dependencies": { + "elliptic": "^6.5.7", + "node-addon-api": "^5.0.0", + "node-gyp-build": "^4.2.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/secp256k1/node_modules/node-addon-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true, + "license": "ISC" + }, + "node_modules/sha.js": { + "version": "2.4.12", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz", + "integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==", + "dev": true, + "license": "(MIT AND BSD-3-Clause)", + "peer": true, + "dependencies": { + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1", + "to-buffer": "^1.2.0" + }, + "bin": { + "sha.js": "bin.js" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/sha1": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/sha1/-/sha1-1.1.1.tgz", + "integrity": "sha512-dZBS6OrMjtgVkopB1Gmo4RQCDKiZsqcpAQpkV/aaj+FCrCg8r4I4qMkDPQjBgLIxlmu9k4nUbWq6ohXahOneYA==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "charenc": ">= 0.0.1", + "crypt": ">= 0.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/shelljs": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + }, + "bin": { + "shjs": "bin/shjs" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/shelljs/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/shelljs/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/shelljs/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/solc": { + "version": "0.8.26", + "resolved": "https://registry.npmjs.org/solc/-/solc-0.8.26.tgz", + "integrity": "sha512-yiPQNVf5rBFHwN6SIf3TUUvVAFKcQqmSUFeq+fb6pNRCo0ZCgpYOZDi3BVoezCPIAcKrVYd/qXlBLUP9wVrZ9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "command-exists": "^1.2.8", + "commander": "^8.1.0", + "follow-redirects": "^1.12.1", + "js-sha3": "0.8.0", + "memorystream": "^0.3.1", + "semver": "^5.5.0", + "tmp": "0.0.33" + }, + "bin": { + "solcjs": "solc.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/solc/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/solidity-coverage": { + "version": "0.8.16", + "resolved": "https://registry.npmjs.org/solidity-coverage/-/solidity-coverage-0.8.16.tgz", + "integrity": "sha512-qKqgm8TPpcnCK0HCDLJrjbOA2tQNEJY4dHX/LSSQ9iwYFS973MwjtgYn2Iv3vfCEQJTj5xtm4cuUMzlJsJSMbg==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "@ethersproject/abi": "^5.0.9", + "@solidity-parser/parser": "^0.20.1", + "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.1", + "jsonschema": "^1.2.4", + "lodash": "^4.17.21", + "mocha": "^10.2.0", + "node-emoji": "^1.10.0", + "pify": "^4.0.1", + "recursive-readdir": "^2.2.2", + "sc-istanbul": "^0.4.5", + "semver": "^7.3.4", + "shelljs": "^0.8.3", + "web3-utils": "^1.3.6" + }, + "bin": { + "solidity-coverage": "plugins/bin.js" + }, + "peerDependencies": { + "hardhat": "^2.11.0" + } + }, + "node_modules/solidity-coverage/node_modules/@solidity-parser/parser": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.20.2.tgz", + "integrity": "sha512-rbu0bzwNvMcwAjH86hiEAcOeRI2EeK8zCkHDrFykh/Al8mvJeFmjy3UrE7GYQjNwOgbGUUtCn5/k8CB8zIu7QA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/solidity-coverage/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/solidity-coverage/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/solidity-coverage/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/solidity-coverage/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/solidity-coverage/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/solidity-coverage/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/solidity-coverage/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/solidity-coverage/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "peer": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/solidity-coverage/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "peer": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/solidity-coverage/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/solidity-coverage/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/source-map": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", + "integrity": "sha512-CBdZ2oa/BHhS4xj5DlhjWNHcan57/5YuvfdLf17iVmIpd9KRm+DFLmC6nBNj+6Ua7Kt3TmOjDpQT1aTYOQtoUA==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "amdefine": ">=0.0.4" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/stacktrace-parser": { + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.11.tgz", + "integrity": "sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.7.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/stacktrace-parser/node_modules/type-fest": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", + "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-format": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/string-format/-/string-format-2.0.0.tgz", + "integrity": "sha512-bbEs3scLeYNXLecRRuk6uJxdXUSj6le/8rNPHChIJTn2V79aXVTR1EH2OH5zLKKoz0V02fOUKZZcw01pLUShZA==", + "dev": true, + "license": "WTFPL OR MIT", + "peer": true + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-hex-prefix": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", + "integrity": "sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "is-hex-prefixed": "1.0.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/sync-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/sync-request/-/sync-request-6.1.0.tgz", + "integrity": "sha512-8fjNkrNlNCrVc/av+Jn+xxqfCjYaBoHqCsDz6mt030UMxJGr+GSfCV1dQt2gRtlL63+VPidwDVLr7V2OcTSdRw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "http-response-object": "^3.0.1", + "sync-rpc": "^1.2.1", + "then-request": "^6.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/sync-rpc": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/sync-rpc/-/sync-rpc-1.3.6.tgz", + "integrity": "sha512-J8jTXuZzRlvU7HemDgHi3pGnh/rkoqR/OZSjhTyyZrEkkYQbk7Z33AXp37mkPfPpfdOuj7Ex3H/TJM1z48uPQw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "get-port": "^3.1.0" + } + }, + "node_modules/table": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", + "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table-layout": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-1.0.2.tgz", + "integrity": "sha512-qd/R7n5rQTRFi+Zf2sk5XVVd9UQl6ZkduPFC3S7WEGJAmetDTjY3qPN50eSKzwuzEyQKy5TN2TiZdkIjos2L6A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "array-back": "^4.0.1", + "deep-extend": "~0.6.0", + "typical": "^5.2.0", + "wordwrapjs": "^4.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/table-layout/node_modules/array-back": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz", + "integrity": "sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/table-layout/node_modules/typical": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz", + "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/then-request": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/then-request/-/then-request-6.0.2.tgz", + "integrity": "sha512-3ZBiG7JvP3wbDzA9iNY5zJQcHL4jn/0BWtXIkagfz7QgOL/LqjCEOBQuJNZfu0XYnv5JhKh+cDxCPM4ILrqruA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/concat-stream": "^1.6.0", + "@types/form-data": "0.0.33", + "@types/node": "^8.0.0", + "@types/qs": "^6.2.31", + "caseless": "~0.12.0", + "concat-stream": "^1.6.0", + "form-data": "^2.2.0", + "http-basic": "^8.1.1", + "http-response-object": "^3.0.1", + "promise": "^8.0.0", + "qs": "^6.4.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/then-request/node_modules/@types/node": { + "version": "8.10.66", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz", + "integrity": "sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/then-request/node_modules/form-data": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.5.tgz", + "integrity": "sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A==", + "dev": true, + "license": "MIT", + "peer": true, + "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" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/to-buffer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.2.tgz", + "integrity": "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "isarray": "^2.0.5", + "safe-buffer": "^5.2.1", + "typed-array-buffer": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/ts-command-line-args": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/ts-command-line-args/-/ts-command-line-args-2.5.1.tgz", + "integrity": "sha512-H69ZwTw3rFHb5WYpQya40YAX2/w7Ut75uUECbgBIsLmM+BNuYnxsltfyyLMxy6sEeKxgijLTnQtLd0nKd6+IYw==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "chalk": "^4.1.0", + "command-line-args": "^5.1.1", + "command-line-usage": "^6.1.0", + "string-format": "^2.0.0" + }, + "bin": { + "write-markdown": "dist/write-markdown.js" + } + }, + "node_modules/ts-essentials": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-7.0.3.tgz", + "integrity": "sha512-8+gr5+lqO3G84KdiTSMRLtuyJ+nTBVRKuCrK4lidMPdVeEp0uqC875uE5NMcaA7YYMN7XsNiFQuMvasF8HT/xQ==", + "dev": true, + "license": "MIT", + "peer": true, + "peerDependencies": { + "typescript": ">=3.7.0" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "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 + } + } + }, + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "dev": true, + "license": "0BSD", + "peer": true + }, + "node_modules/tsort": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/tsort/-/tsort-0.0.1.tgz", + "integrity": "sha512-Tyrf5mxF8Ofs1tNoxA13lFeZ2Zrbd6cKbuH3V+MQ5sb6DtBj5FjrXVsRWT8YvNAQTqNoz66dz1WsbigI22aEnw==", + "dev": true, + "license": "MIT" + }, + "node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typechain": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/typechain/-/typechain-8.3.2.tgz", + "integrity": "sha512-x/sQYr5w9K7yv3es7jo4KTX05CLxOf7TRWwoHlrjRh8H82G64g+k7VuWPJlgMo6qrjfCulOdfBjiaDtmhFYD/Q==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/prettier": "^2.1.1", + "debug": "^4.3.1", + "fs-extra": "^7.0.0", + "glob": "7.1.7", + "js-sha3": "^0.8.0", + "lodash": "^4.17.15", + "mkdirp": "^1.0.4", + "prettier": "^2.3.1", + "ts-command-line-args": "^2.2.0", + "ts-essentials": "^7.0.1" + }, + "bin": { + "typechain": "dist/cli/cli.js" + }, + "peerDependencies": { + "typescript": ">=4.3.0" + } + }, + "node_modules/typechain/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/typechain/node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/typechain/node_modules/glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/typechain/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "peer": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/typechain/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/typechain/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/typechain/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typical": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz", + "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "peer": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/undici": { + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz", + "integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", + "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/web3-utils": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.10.4.tgz", + "integrity": "sha512-tsu8FiKJLk2PzhDl9fXbGUWTkkVXYhtTA+SmEFkKft+9BgwLxfCRpU96sWv7ICC8zixBNd3JURVoiR3dUXgP8A==", + "dev": true, + "license": "LGPL-3.0", + "peer": true, + "dependencies": { + "@ethereumjs/util": "^8.1.0", + "bn.js": "^5.2.1", + "ethereum-bloom-filters": "^1.0.6", + "ethereum-cryptography": "^2.1.2", + "ethjs-unit": "0.1.6", + "number-to-bn": "1.7.0", + "randombytes": "^2.1.0", + "utf8": "3.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-utils/node_modules/@ethereumjs/rlp": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-4.0.1.tgz", + "integrity": "sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw==", + "dev": true, + "license": "MPL-2.0", + "peer": true, + "bin": { + "rlp": "bin/rlp" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/web3-utils/node_modules/@ethereumjs/util": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/util/-/util-8.1.0.tgz", + "integrity": "sha512-zQ0IqbdX8FZ9aw11vP+dZkKDkS+kgIvQPHnSAXzP9pLu+Rfu3D3XEeLbicvoXJTYnhZiPmsZUxgdzXwNKxRPbA==", + "dev": true, + "license": "MPL-2.0", + "peer": true, + "dependencies": { + "@ethereumjs/rlp": "^4.0.1", + "ethereum-cryptography": "^2.0.0", + "micro-ftch": "^0.3.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/web3-utils/node_modules/@noble/curves": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", + "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@noble/hashes": "1.4.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/web3-utils/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/web3-utils/node_modules/ethereum-cryptography": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz", + "integrity": "sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@noble/curves": "1.4.2", + "@noble/hashes": "1.4.0", + "@scure/bip32": "1.4.0", + "@scure/bip39": "1.3.0" + } + }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "dev": true, + "license": "MIT", + "peer": true, + "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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/wordwrapjs": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-4.0.1.tgz", + "integrity": "sha512-kKlNACbvHrkpIw6oPeYDSmdCTu2hdMHoyXLTcUKala++lx5Y+wjJ/e474Jqv5abnVmwxw08DiTuHmw69lJGksA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "reduce-flatten": "^2.0.0", + "typical": "^5.2.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/wordwrapjs/node_modules/typical": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz", + "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/workerpool": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "dev": true, + "license": "MIT", + "peer": true, + "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 + } + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/contracts/package.json b/contracts/package.json new file mode 100644 index 0000000..ed58af1 --- /dev/null +++ b/contracts/package.json @@ -0,0 +1,21 @@ +{ + "name": "pelagos-bridge-contracts", + "version": "1.0.0", + "description": "Pelagos Bridge Smart Contracts", + "scripts": { + "test": "hardhat test", + "compile": "hardhat compile", + "deploy:sepolia": "hardhat run scripts/deploy.js --network sepolia", + "deploy:amoy": "hardhat run scripts/deploy.js --network amoy", + "verify:sepolia": "hardhat verify --network sepolia", + "verify:amoy": "hardhat verify --network amoy" + }, + "devDependencies": { + "@nomicfoundation/hardhat-toolbox": "^4.0.0", + "hardhat": "^2.19.0" + }, + "dependencies": { + "@openzeppelin/contracts": "^5.4.0", + "dotenv": "^17.2.3" + } +} diff --git a/contracts/scripts/deploy.js b/contracts/scripts/deploy.js new file mode 100644 index 0000000..f72b91e --- /dev/null +++ b/contracts/scripts/deploy.js @@ -0,0 +1,175 @@ +const hre = require("hardhat"); +const fs = require("fs"); +const path = require("path"); + +async function main() { + const [deployer] = await hre.ethers.getSigners(); + const network = hre.network.name; + const chainId = Number((await hre.ethers.provider.getNetwork()).chainId); + + console.log("Deploying contracts with account:", deployer.address); + console.log("Network:", network); + console.log("Chain ID:", chainId); + + // Deploy generic Bridge (no constructor parameters) + console.log("\nDeploying generic Bridge contract..."); + console.log(" Supports any ERC20 token or native gas token"); + console.log(" Use address(0) for native tokens"); + + const Bridge = await hre.ethers.getContractFactory("Bridge"); + const bridge = await Bridge.deploy(); + await bridge.waitForDeployment(); + + const bridgeAddress = await bridge.getAddress(); + console.log("Bridge deployed to:", bridgeAddress); + + // Determine liquidity token based on network + let liquidityToken; + let liquidityInstructions; + + if (network === "sepolia") { + // Sepolia: POL is ERC20 token + liquidityToken = process.env.POL_TOKEN_SEPOLIA || "0x6a7c3f4b0651d6da389ad1d11d962ea458cdca70"; + console.log("\n⚠️ MANUAL STEP REQUIRED:"); + console.log("To add 50 POL (ERC20) liquidity on Sepolia:"); + console.log(`\n1. Approve Bridge to spend POL:`); + console.log(`cast send ${liquidityToken} "approve(address,uint256)" ${bridgeAddress} 50000000000000000000 --private-key $PRIVATE_KEY --rpc-url $SEPOLIA_RPC_URL\n`); + console.log(`2. Add liquidity:`); + console.log(`cast send ${bridgeAddress} "addLiquidity(address,uint256)" ${liquidityToken} 50000000000000000000 --private-key $PRIVATE_KEY --rpc-url $SEPOLIA_RPC_URL\n`); + } else if (network === "stavanger") { + // Stavanger: POL is native token (address(0)) + liquidityToken = hre.ethers.ZeroAddress; + console.log("\nAdding 50 native POL liquidity to bridge..."); + const liquidityAmount = hre.ethers.parseEther("50"); + + // Call addLiquidity with native token (address(0)) + const tx = await bridge.addLiquidity(hre.ethers.ZeroAddress, liquidityAmount, { + value: liquidityAmount + }); + await tx.wait(); + console.log("βœ… Added 50 POL liquidity to bridge"); + + // Check balance + const balance = await hre.ethers.provider.getBalance(bridgeAddress); + console.log(`Bridge balance: ${hre.ethers.formatEther(balance)} POL`); + } else { + liquidityToken = hre.ethers.ZeroAddress; + console.log("\n⚠️ Note: Add liquidity manually for this network"); + } + + // Get Pelagos contract address for this network + const pelagosAddress = process.env[`PELAGOS_${network.toUpperCase()}`] || process.env.PELAGOS_CONTRACT || hre.ethers.ZeroAddress; + if (pelagosAddress !== hre.ethers.ZeroAddress) { + console.log("\nSetting Pelagos contract address:", pelagosAddress); + const setPelagosTx = await bridge.setPelagosContract(pelagosAddress); + await setPelagosTx.wait(); + console.log("βœ… Pelagos contract set"); + } else { + console.log("\n⚠️ Note: Pelagos contract address not set. Set it manually with:"); + console.log(`cast send ${bridgeAddress} "setPelagosContract(address)" --private-key $PRIVATE_KEY --rpc-url $RPC_URL`); + } + + // Save deployment info + const deploymentInfo = { + network: network, + chainId: chainId, + deployer: deployer.address, + bridge: bridgeAddress, + bridgeType: "generic", + supportedTokens: "any ERC20 or native (address(0))", + pelagosContract: pelagosAddress, + deployedAt: new Date().toISOString(), + blockNumber: await hre.ethers.provider.getBlockNumber() + }; + + // Create config directory if it doesn't exist + const configDir = path.join(__dirname, "../../config"); + if (!fs.existsSync(configDir)) { + fs.mkdirSync(configDir, { recursive: true }); + } + + // Load existing bridge contracts config or create new one + const bridgeContractsPath = path.join(configDir, "bridge_contracts.json"); + let bridgeContracts = { + version: "1.0.0", + deployments: {} + }; + + if (fs.existsSync(bridgeContractsPath)) { + bridgeContracts = JSON.parse(fs.readFileSync(bridgeContractsPath, "utf8")); + } + + // Update with new deployment + bridgeContracts.deployments[chainId.toString()] = { + name: network, + bridge: bridgeAddress, + bridgeType: "generic", + supportedTokens: "any ERC20 or native (address(0))", + pelagosContract: pelagosAddress, + deployedAt: deploymentInfo.blockNumber, + deployer: deployer.address + }; + + // Save updated config + fs.writeFileSync( + bridgeContractsPath, + JSON.stringify(bridgeContracts, null, 2) + ); + + console.log("\nDeployment info saved to:", bridgeContractsPath); + + // Save deployment details for this specific deployment + const deploymentsDir = path.join(__dirname, "../deployments"); + if (!fs.existsSync(deploymentsDir)) { + fs.mkdirSync(deploymentsDir, { recursive: true }); + } + + const deploymentFile = path.join(deploymentsDir, `${network}-${Date.now()}.json`); + fs.writeFileSync(deploymentFile, JSON.stringify(deploymentInfo, null, 2)); + console.log("Detailed deployment saved to:", deploymentFile); + + // Print summary + console.log("\n=== Deployment Summary ==="); + console.log("Network:", network); + console.log("Chain ID:", chainId); + console.log("Bridge:", bridgeAddress); + console.log("Bridge Type: Generic (supports any token)"); + console.log("Native Token: address(0)"); + console.log("ERC20 Tokens: Any valid token contract"); + console.log("Pelagos Contract:", pelagosAddress === hre.ethers.ZeroAddress ? "Not set" : pelagosAddress); + console.log("Deployed at block:", deploymentInfo.blockNumber); + console.log("========================\n"); + + // Verification instructions + if (network !== "localhost" && network !== "hardhat") { + console.log("To verify contract on block explorer:"); + console.log(`npx hardhat verify --network ${network} ${bridgeAddress}`); + } + + // Next steps + console.log("\n=== Next Steps ==="); + console.log("1. Update config/consensus_chains.json:"); + console.log(` - Set StartBlock to: ${deploymentInfo.blockNumber}`); + console.log(` - Set Bridge address to: ${bridgeAddress}`); + console.log("\n2. Update config/ext_networks.json:"); + console.log(` - Set contractAddress to: ${bridgeAddress}`); + console.log("\n3. Update application/state_transition.go:"); + if (network === "sepolia") { + console.log(` - BridgeContractAddressSepolia = "${bridgeAddress}"`); + } else if (network === "stavanger") { + console.log(` - BridgeContractAddressStavanger = "${bridgeAddress}"`); + } + console.log("\n4. Set Pelagos contract address (if not set):"); + console.log(` bridge.setPelagosContract()`); + console.log("\n5. Bridge Usage:"); + console.log(" - For native tokens: bridgeAsset(address(0), amount, destChainId, recipient) + send msg.value"); + console.log(" - For ERC20 tokens: approve first, then bridgeAsset(tokenAddress, amount, destChainId, recipient)"); + console.log("==================\n"); +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/contracts/test/Bridge.test.js b/contracts/test/Bridge.test.js new file mode 100644 index 0000000..0cce767 --- /dev/null +++ b/contracts/test/Bridge.test.js @@ -0,0 +1,403 @@ +const { expect } = require("chai"); +const { ethers } = require("hardhat"); + +describe("Bridge", function () { + let bridge; + let mockToken; + let owner; + let validator; + let user1; + let user2; + + const SEPOLIA_CHAIN_ID = 11155111; + const AMOY_CHAIN_ID = 80002; + + beforeEach(async function () { + [owner, validator, user1, user2] = await ethers.getSigners(); + + // Deploy mock token + const MockERC20 = await ethers.getContractFactory("MockERC20"); + mockToken = await MockERC20.deploy("Test USDT", "USDT", 6); + await mockToken.waitForDeployment(); + + // Mint tokens to user1 + await mockToken.mint(user1.address, ethers.parseUnits("10000", 6)); + + // Deploy bridge + const Bridge = await ethers.getContractFactory("Bridge"); + bridge = await Bridge.deploy(validator.address); + await bridge.waitForDeployment(); + }); + + describe("Deployment", function () { + it("Should set the correct validator", async function () { + expect(await bridge.validator()).to.equal(validator.address); + }); + + it("Should set the correct chainId", async function () { + expect(await bridge.chainId()).to.equal(31337); // Hardhat network + }); + + it("Should set the correct owner", async function () { + expect(await bridge.owner()).to.equal(owner.address); + }); + }); + + describe("BridgeAsset", function () { + it("Should lock tokens and emit BridgeInitiated event", async function () { + const amount = ethers.parseUnits("1000", 6); + + // Approve bridge to spend tokens + await mockToken.connect(user1).approve(await bridge.getAddress(), amount); + + // Bridge tokens + const tx = await bridge.connect(user1).bridgeAsset( + AMOY_CHAIN_ID, + user2.address, + await mockToken.getAddress(), + amount + ); + + const receipt = await tx.wait(); + const event = receipt.logs.find( + log => log.fragment && log.fragment.name === "BridgeInitiated" + ); + + expect(event).to.not.be.undefined; + expect(event.args.sourceChainId).to.equal(31337); + expect(event.args.destChainId).to.equal(AMOY_CHAIN_ID); + expect(event.args.token).to.equal(await mockToken.getAddress()); + expect(event.args.amount).to.equal(amount); + expect(event.args.sender).to.equal(user1.address); + expect(event.args.recipient).to.equal(user2.address); + + // Check tokens were locked + expect(await mockToken.balanceOf(await bridge.getAddress())).to.equal(amount); + }); + + it("Should revert if destination chain is the same as source", async function () { + const amount = ethers.parseUnits("1000", 6); + await mockToken.connect(user1).approve(await bridge.getAddress(), amount); + + await expect( + bridge.connect(user1).bridgeAsset( + 31337, // Same as source + user2.address, + await mockToken.getAddress(), + amount + ) + ).to.be.revertedWithCustomError(bridge, "InvalidDestinationChain"); + }); + + it("Should revert if amount is zero", async function () { + await expect( + bridge.connect(user1).bridgeAsset( + AMOY_CHAIN_ID, + user2.address, + await mockToken.getAddress(), + 0 + ) + ).to.be.revertedWithCustomError(bridge, "InvalidAmount"); + }); + + it("Should revert if recipient is zero address", async function () { + const amount = ethers.parseUnits("1000", 6); + await mockToken.connect(user1).approve(await bridge.getAddress(), amount); + + await expect( + bridge.connect(user1).bridgeAsset( + AMOY_CHAIN_ID, + ethers.ZeroAddress, + await mockToken.getAddress(), + amount + ) + ).to.be.revertedWithCustomError(bridge, "InvalidRecipient"); + }); + + it("Should revert if token is zero address", async function () { + const amount = ethers.parseUnits("1000", 6); + + await expect( + bridge.connect(user1).bridgeAsset( + AMOY_CHAIN_ID, + user2.address, + ethers.ZeroAddress, + amount + ) + ).to.be.revertedWithCustomError(bridge, "InvalidToken"); + }); + + it("Should increment bridge nonce", async function () { + const amount = ethers.parseUnits("1000", 6); + await mockToken.connect(user1).approve(await bridge.getAddress(), amount * 2n); + + expect(await bridge.bridgeNonce()).to.equal(0); + + await bridge.connect(user1).bridgeAsset( + AMOY_CHAIN_ID, + user2.address, + await mockToken.getAddress(), + amount + ); + expect(await bridge.bridgeNonce()).to.equal(1); + + await bridge.connect(user1).bridgeAsset( + AMOY_CHAIN_ID, + user2.address, + await mockToken.getAddress(), + amount + ); + expect(await bridge.bridgeNonce()).to.equal(2); + }); + }); + + describe("ClaimAsset", function () { + let bridgeId; + const amount = ethers.parseUnits("500", 6); + + beforeEach(async function () { + // Add liquidity to bridge for claims + await mockToken.mint(await bridge.getAddress(), ethers.parseUnits("5000", 6)); + + // Generate a unique bridge ID + bridgeId = ethers.keccak256( + ethers.AbiCoder.defaultAbiCoder().encode( + ["uint256", "uint256", "address", "uint256", "address", "uint256"], + [SEPOLIA_CHAIN_ID, 31337, await mockToken.getAddress(), amount, user2.address, Date.now()] + ) + ); + }); + + it("Should claim tokens and emit AssetClaimed event", async function () { + const tx = await bridge.connect(validator).claimAsset( + bridgeId, + SEPOLIA_CHAIN_ID, + await mockToken.getAddress(), + amount, + user2.address + ); + + const receipt = await tx.wait(); + const event = receipt.logs.find( + log => log.fragment && log.fragment.name === "AssetClaimed" + ); + + expect(event).to.not.be.undefined; + expect(event.args.bridgeId).to.equal(bridgeId); + expect(event.args.recipient).to.equal(user2.address); + expect(event.args.token).to.equal(await mockToken.getAddress()); + expect(event.args.amount).to.equal(amount); + + // Check tokens were transferred + expect(await mockToken.balanceOf(user2.address)).to.equal(amount); + }); + + it("Should mark bridge as claimed", async function () { + await bridge.connect(validator).claimAsset( + bridgeId, + SEPOLIA_CHAIN_ID, + await mockToken.getAddress(), + amount, + user2.address + ); + + expect(await bridge.isClaimed(bridgeId)).to.be.true; + }); + + it("Should revert if already claimed", async function () { + await bridge.connect(validator).claimAsset( + bridgeId, + SEPOLIA_CHAIN_ID, + await mockToken.getAddress(), + amount, + user2.address + ); + + await expect( + bridge.connect(validator).claimAsset( + bridgeId, + SEPOLIA_CHAIN_ID, + await mockToken.getAddress(), + amount, + user2.address + ) + ).to.be.revertedWithCustomError(bridge, "BridgeAlreadyClaimed"); + }); + + it("Should revert if not called by validator", async function () { + await expect( + bridge.connect(user1).claimAsset( + bridgeId, + SEPOLIA_CHAIN_ID, + await mockToken.getAddress(), + amount, + user2.address + ) + ).to.be.revertedWithCustomError(bridge, "Unauthorized"); + }); + + it("Should revert if insufficient liquidity", async function () { + const largeAmount = ethers.parseUnits("10000", 6); + const newBridgeId = ethers.keccak256( + ethers.AbiCoder.defaultAbiCoder().encode( + ["uint256", "uint256", "address", "uint256", "address", "uint256"], + [SEPOLIA_CHAIN_ID, 31337, await mockToken.getAddress(), largeAmount, user2.address, Date.now() + 1000] + ) + ); + + await expect( + bridge.connect(validator).claimAsset( + newBridgeId, + SEPOLIA_CHAIN_ID, + await mockToken.getAddress(), + largeAmount, + user2.address + ) + ).to.be.revertedWithCustomError(bridge, "InsufficientLiquidity"); + }); + }); + + describe("Liquidity Management", function () { + it("Should allow anyone to add liquidity", async function () { + const amount = ethers.parseUnits("1000", 6); + await mockToken.connect(user1).approve(await bridge.getAddress(), amount); + + await expect( + bridge.connect(user1).addLiquidity(await mockToken.getAddress(), amount) + ).to.emit(bridge, "LiquidityAdded") + .withArgs(await mockToken.getAddress(), amount, user1.address); + + expect(await bridge.getLiquidity(await mockToken.getAddress())).to.equal(amount); + }); + + it("Should allow owner to remove liquidity", async function () { + const amount = ethers.parseUnits("1000", 6); + await mockToken.mint(await bridge.getAddress(), amount); + + await expect( + bridge.connect(owner).removeLiquidity( + await mockToken.getAddress(), + amount, + user1.address + ) + ).to.emit(bridge, "LiquidityRemoved") + .withArgs(await mockToken.getAddress(), amount, user1.address); + + expect(await mockToken.balanceOf(user1.address)).to.equal( + ethers.parseUnits("10000", 6) + amount + ); + }); + + it("Should not allow non-owner to remove liquidity", async function () { + const amount = ethers.parseUnits("1000", 6); + await mockToken.mint(await bridge.getAddress(), amount); + + await expect( + bridge.connect(user1).removeLiquidity( + await mockToken.getAddress(), + amount, + user1.address + ) + ).to.be.revertedWithCustomError(bridge, "OwnableUnauthorizedAccount"); + }); + }); + + describe("Admin Functions", function () { + it("Should allow owner to update validator", async function () { + await expect( + bridge.connect(owner).updateValidator(user1.address) + ).to.emit(bridge, "ValidatorUpdated") + .withArgs(validator.address, user1.address); + + expect(await bridge.validator()).to.equal(user1.address); + }); + + it("Should allow owner to pause", async function () { + await bridge.connect(owner).pause(); + expect(await bridge.paused()).to.be.true; + + const amount = ethers.parseUnits("1000", 6); + await mockToken.connect(user1).approve(await bridge.getAddress(), amount); + + await expect( + bridge.connect(user1).bridgeAsset( + AMOY_CHAIN_ID, + user2.address, + await mockToken.getAddress(), + amount + ) + ).to.be.revertedWithCustomError(bridge, "EnforcedPause"); + }); + + it("Should allow owner to unpause", async function () { + await bridge.connect(owner).pause(); + await bridge.connect(owner).unpause(); + expect(await bridge.paused()).to.be.false; + }); + + it("Should allow emergency withdrawal when paused", async function () { + const amount = ethers.parseUnits("1000", 6); + await mockToken.mint(await bridge.getAddress(), amount); + + await bridge.connect(owner).pause(); + await bridge.connect(owner).emergencyWithdraw( + await mockToken.getAddress(), + amount, + user1.address + ); + + expect(await mockToken.balanceOf(user1.address)).to.equal( + ethers.parseUnits("10000", 6) + amount + ); + }); + + it("Should not allow emergency withdrawal when not paused", async function () { + const amount = ethers.parseUnits("1000", 6); + await mockToken.mint(await bridge.getAddress(), amount); + + await expect( + bridge.connect(owner).emergencyWithdraw( + await mockToken.getAddress(), + amount, + user1.address + ) + ).to.be.revertedWithCustomError(bridge, "ExpectedPause"); + }); + }); + + describe("View Functions", function () { + it("Should return bridge data", async function () { + const amount = ethers.parseUnits("1000", 6); + await mockToken.connect(user1).approve(await bridge.getAddress(), amount); + + const tx = await bridge.connect(user1).bridgeAsset( + AMOY_CHAIN_ID, + user2.address, + await mockToken.getAddress(), + amount + ); + + const receipt = await tx.wait(); + const event = receipt.logs.find( + log => log.fragment && log.fragment.name === "BridgeInitiated" + ); + const bridgeId = event.args.bridgeId; + + const bridgeData = await bridge.getBridge(bridgeId); + expect(bridgeData.sourceChainId).to.equal(31337); + expect(bridgeData.destChainId).to.equal(AMOY_CHAIN_ID); + expect(bridgeData.token).to.equal(await mockToken.getAddress()); + expect(bridgeData.amount).to.equal(amount); + expect(bridgeData.sender).to.equal(user1.address); + expect(bridgeData.recipient).to.equal(user2.address); + expect(bridgeData.claimed).to.be.false; + }); + + it("Should return correct liquidity", async function () { + const amount = ethers.parseUnits("1000", 6); + await mockToken.mint(await bridge.getAddress(), amount); + + expect(await bridge.getLiquidity(await mockToken.getAddress())).to.equal(amount); + }); + }); +}); diff --git a/docker-compose.yml b/docker-compose.yml index 611e7fb..365c3b9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,12 +5,12 @@ services: working_dir: /app volumes: - ./data:/data - # - ./pelacli.yaml:/config/pelacli.yaml + - ./pelacli.yaml:/config/pelacli.yaml:ro ports: - "8081:8081" command: - consensus - # - --config=/config/pelacli.yaml + - --config=/config/pelacli.yaml appchain: build: @@ -21,22 +21,79 @@ services: image: appchain:latest volumes: - ./data:/data - # - ./config.yaml:/config/config.yaml + - ./config.yaml:/config/config.yaml:ro ports: - "9090:9090" - "8080:8080" + - "9091:9091" depends_on: - pelacli - # command: - # - -config=/config/config.yaml + command: + - -config=/config/config.yaml + + frontend: + image: nginx:alpine + volumes: + - ./frontend:/usr/share/nginx/html:ro + ports: + - "3000:80" + depends_on: + - appchain explorer: image: pelagosnetwork/explorer:latest ports: - - "3000:3000" + - "3001:3000" environment: - - RPC_URL=http://localhost:8080/rpc - - EXTERNAL_RPC_URL=http://localhost:8081/rpc + - RPC_URL=http://appchain:8080/rpc + - EXTERNAL_RPC_URL=http://pelacli:8081/rpc depends_on: - appchain - pelacli + + prometheus: + profiles: ["monitoring"] + image: prom/prometheus:latest + volumes: + - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml:ro + - ./monitoring/alert_rules.yml:/etc/prometheus/alert_rules.yml:ro + - prometheus_data:/prometheus + ports: + - "9092:9090" + command: + - '--config.file=/etc/prometheus/prometheus.yml' + - '--storage.tsdb.path=/prometheus' + - '--web.enable-lifecycle' + depends_on: + - appchain + + alertmanager: + profiles: ["monitoring"] + image: prom/alertmanager:latest + volumes: + - ./monitoring/alertmanager.yml:/etc/alertmanager/alertmanager.yml:ro + - alertmanager_data:/alertmanager + ports: + - "9093:9093" + command: + - '--config.file=/etc/alertmanager/alertmanager.yml' + - '--storage.path=/alertmanager' + + grafana: + profiles: ["monitoring"] + image: grafana/grafana:latest + volumes: + - ./monitoring/grafana/provisioning:/etc/grafana/provisioning:ro + - ./monitoring/grafana/dashboards:/var/lib/grafana/dashboards:ro + - grafana_data:/var/lib/grafana + ports: + - "3002:3000" + environment: + - GF_SECURITY_ADMIN_USER=admin + - GF_SECURITY_ADMIN_PASSWORD=admin + - GF_USERS_ALLOW_SIGN_UP=false + +volumes: + prometheus_data: + alertmanager_data: + grafana_data: diff --git a/frontend/Pelagos_primary_logo.svg b/frontend/Pelagos_primary_logo.svg new file mode 100644 index 0000000..abf90f1 --- /dev/null +++ b/frontend/Pelagos_primary_logo.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/app.js b/frontend/app.js new file mode 100644 index 0000000..7a828d6 --- /dev/null +++ b/frontend/app.js @@ -0,0 +1,977 @@ +// Auto-refresh intervals +let historyRefreshInterval = null; +let balanceRefreshInterval = null; +const HISTORY_REFRESH_MS = 2000; // 2 seconds +const BALANCE_REFRESH_MS = 5000; // 5 seconds + +// Configuration - DEPLOYED AND READY! +const CONFIG = { + BRIDGE_SEPOLIA: '0x844E740Ea7F404c6208fd85Ee6114a14F8037df7', // Sepolia bridge address + BRIDGE_STAVANGER: '0x3C1c8351a09DB0300786148B56EcB7be2FaA322e', // Stavanger bridge address + POL_TOKEN_SEPOLIA: '0x6a7c3f4b0651d6da389ad1d11d962ea458cdca70', + SEPOLIA_CHAIN_ID: 11155111, + STAVANGER_CHAIN_ID: 50591822, + SEPOLIA_RPC: 'https://eth-sepolia.g.alchemy.com/v2/bEzb7Rb14hzR79tLw-gfy', + STAVANGER_RPC: 'https://rpc.stavanger.gateway.fm', + APPCHAIN_RPC: 'http://localhost:8080/rpc', // Pelagos bridge appchain RPC +}; + +// Network metadata +const NETWORKS = { + sepolia: { + name: 'Sepolia', + chainId: CONFIG.SEPOLIA_CHAIN_ID, + icon: "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'%3E%3Ccircle cx='16' cy='16' r='16' fill='%23627eea'/%3E%3Cpath d='M16 4L15.5 5.5L15.5 20.5L16 21L24 16.5z' fill='white'/%3E%3Cpath d='M16 4L8 16.5L16 21z' fill='%23c1cde9'/%3E%3C/svg%3E", + rpcUrl: CONFIG.SEPOLIA_RPC, + bridgeAddress: CONFIG.BRIDGE_SEPOLIA, + tokenAddress: CONFIG.POL_TOKEN_SEPOLIA, + tokenType: 'ERC20' + }, + stavanger: { + name: 'Stavanger', + chainId: CONFIG.STAVANGER_CHAIN_ID, + icon: "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'%3E%3Ccircle cx='16' cy='16' r='16' fill='%238247e5'/%3E%3Cpath d='M10 8L16 12L22 8L16 4z' fill='white'/%3E%3Cpath d='M10 16L16 20L22 16L16 12z' fill='%23b794f6'/%3E%3Cpath d='M10 24L16 28L22 24L16 20z' fill='white'/%3E%3C/svg%3E", + rpcUrl: CONFIG.STAVANGER_RPC, + bridgeAddress: CONFIG.BRIDGE_STAVANGER, + tokenAddress: '0x0000000000000000000000000000000000000000', // Native + tokenType: 'Native' + } +}; + +// Global state +let provider = null; +let signer = null; +let account = null; +let currentFromNetwork = null; +let currentToNetwork = null; +let modalTarget = null; +let fromBalance = null; +let toBalance = null; +let bridgeLiquidity = null; +let walletEventsBound = false; // Prevent duplicate event listeners + +// Initialize +document.addEventListener('DOMContentLoaded', () => { + // Check if ethers is loaded + if (typeof ethers === 'undefined') { + console.error('Ethers.js not loaded! Please check your internet connection.'); + const statusDiv = document.getElementById('statusMessage'); + if (statusDiv) { + statusDiv.className = 'status-message error'; + statusDiv.innerHTML = 'Error: Failed to load required libraries. Please refresh the page.'; + statusDiv.style.display = 'block'; + } + return; + } + + // Set default route: Sepolia β†’ Stavanger + setDefaultNetworks(); + + setupEventListeners(); + checkWalletConnection(); + loadHistory(); +}); + +function setDefaultNetworks() { + currentFromNetwork = 'sepolia'; + currentToNetwork = 'stavanger'; + + const fromNetwork = NETWORKS[currentFromNetwork]; + const toNetwork = NETWORKS[currentToNetwork]; + + document.getElementById('fromIcon').src = fromNetwork.icon; + document.getElementById('fromName').textContent = fromNetwork.name; + document.getElementById('toIcon').src = toNetwork.icon; + document.getElementById('toName').textContent = toNetwork.name; +} + +function setupEventListeners() { + // Tab switching + document.querySelectorAll('.nav-link').forEach(link => { + link.addEventListener('click', (e) => { + const tab = e.target.dataset.tab; + switchTab(tab); + }); + }); + + // Amount input validation + document.getElementById('amountInput').addEventListener('input', () => { + updateBridgeSummary(); + validateBridgeButton(); + }); + + // Recipient input + document.getElementById('recipientInput').addEventListener('input', (e) => { + // Clear error state when typing + e.target.classList.remove('error'); + e.target.placeholder = 'Enter address'; + validateBridgeButton(); + }); +} + +function switchTab(tab) { + // Update nav links + document.querySelectorAll('.nav-link').forEach(link => { + link.classList.remove('active'); + if (link.dataset.tab === tab) { + link.classList.add('active'); + } + }); + + // Update tab content + document.querySelectorAll('.tab-content').forEach(content => { + content.classList.remove('active'); + }); + document.getElementById(`tab-${tab}`).classList.add('active'); +} + +// Wallet Connection +function toggleWallet() { + if (account) { + disconnectWallet(); + } else { + connectWallet(); + } +} + +async function connectWallet() { + try { + if (!window.ethereum) { + showStatus('Please install MetaMask!', 'error'); + return; + } + + // Clear disconnected flag when user manually connects + localStorage.removeItem('walletDisconnected'); + + // Request accounts + const accounts = await window.ethereum.request({ + method: 'eth_requestAccounts' + }); + + provider = new ethers.providers.Web3Provider(window.ethereum); + signer = provider.getSigner(); + account = accounts[0]; + + // Update UI + updateWalletUI(); + + // Bind wallet events only once to prevent accumulation + if (!walletEventsBound) { + window.ethereum.on('accountsChanged', handleAccountsChanged); + window.ethereum.on('chainChanged', handleChainChanged); + walletEventsBound = true; + } + + showStatus('Wallet connected successfully!', 'success'); + updateBalances(); + updateFaucetBalance(); + + // Start balance auto-refresh + if (!balanceRefreshInterval) { + balanceRefreshInterval = setInterval(updateBalances, BALANCE_REFRESH_MS); + } + + } catch (error) { + console.error('Wallet connection error:', error); + showStatus('Failed to connect wallet: ' + error.message, 'error'); + } +} + +function handleAccountsChanged(accounts) { + if (accounts.length === 0) { + disconnectWallet(); + } else { + account = accounts[0]; + updateWalletUI(); + updateBalances(); + } +} + +function handleChainChanged() { + provider = new ethers.providers.Web3Provider(window.ethereum); + signer = provider.getSigner(); + updateBalances(); +} + +async function checkWalletConnection() { + // Don't auto-connect if user manually disconnected + if (localStorage.getItem('walletDisconnected') === 'true') { + return; + } + if (window.ethereum) { + const accounts = await window.ethereum.request({ method: 'eth_accounts' }); + if (accounts.length > 0) { + connectWallet(); + } + } +} + +function updateWalletUI() { + const walletBtn = document.getElementById('walletBtn'); + const changeBtn = document.getElementById('btnChangeRecipient'); + walletBtn.textContent = `${account.substring(0, 6)}...${account.substring(38)}`; + walletBtn.classList.add('connected'); + changeBtn.style.display = 'inline'; + updateDestinationAddress(); + validateBridgeButton(); +} + +function updateDestinationAddress() { + const destinationValue = document.getElementById('destinationValue'); + const recipientInput = document.getElementById('recipientInput'); + const customRecipient = recipientInput.value.trim(); + + if (customRecipient && ethers.utils.isAddress(customRecipient)) { + destinationValue.textContent = `${customRecipient.substring(0, 6)}...${customRecipient.substring(38)}`; + } else if (account) { + destinationValue.textContent = `${account.substring(0, 6)}...${account.substring(38)} (you)`; + } else { + destinationValue.textContent = 'Connect wallet'; + } +} + +function toggleRecipientEdit() { + const destinationValue = document.getElementById('destinationValue'); + const recipientInput = document.getElementById('recipientInput'); + const changeBtn = document.getElementById('btnChangeRecipient'); + const isEditing = recipientInput.style.display !== 'none'; + + if (isEditing) { + const inputValue = recipientInput.value.trim(); + + // Validate if user entered something + if (inputValue && !ethers.utils.isAddress(inputValue)) { + // Invalid address - show error and keep editing + recipientInput.classList.add('error'); + recipientInput.placeholder = 'Invalid address'; + return; + } + + // Valid or empty - close editing + recipientInput.classList.remove('error'); + recipientInput.placeholder = 'Enter address'; + recipientInput.style.display = 'none'; + destinationValue.style.display = 'inline'; + changeBtn.textContent = 'Change'; + updateDestinationAddress(); + } else { + // Start editing - hide address and show input + destinationValue.style.display = 'none'; + recipientInput.style.display = 'inline-block'; + recipientInput.focus(); + changeBtn.textContent = 'Done'; + } +} + +function disconnectWallet() { + provider = null; + signer = null; + account = null; + localStorage.setItem('walletDisconnected', 'true'); + const walletBtn = document.getElementById('walletBtn'); + const changeBtn = document.getElementById('btnChangeRecipient'); + walletBtn.textContent = 'Connect Wallet'; + walletBtn.classList.remove('connected'); + changeBtn.style.display = 'none'; + // Reset recipient input state + const recipientInput = document.getElementById('recipientInput'); + const destinationValue = document.getElementById('destinationValue'); + recipientInput.style.display = 'none'; + recipientInput.value = ''; + destinationValue.style.display = 'inline'; + updateDestinationAddress(); + validateBridgeButton(); +} + +// Network Selection +function showNetworkModal(target) { + modalTarget = target; + document.getElementById('networkModal').classList.add('active'); +} + +function closeNetworkModal() { + document.getElementById('networkModal').classList.remove('active'); +} + +function selectNetwork(networkKey, isInitial = false) { + const network = NETWORKS[networkKey]; + + // Automatically select the opposite network + const oppositeNetwork = networkKey === 'sepolia' ? 'stavanger' : 'sepolia'; + const oppositeNetworkData = NETWORKS[oppositeNetwork]; + + if (modalTarget === 'from') { + currentFromNetwork = networkKey; + document.getElementById('fromIcon').src = network.icon; + document.getElementById('fromName').textContent = network.name; + + // Automatically set the opposite network as "To" + currentToNetwork = oppositeNetwork; + document.getElementById('toIcon').src = oppositeNetworkData.icon; + document.getElementById('toName').textContent = oppositeNetworkData.name; + } else if (modalTarget === 'to') { + currentToNetwork = networkKey; + document.getElementById('toIcon').src = network.icon; + document.getElementById('toName').textContent = network.name; + + // Automatically set the opposite network as "From" + currentFromNetwork = oppositeNetwork; + document.getElementById('fromIcon').src = oppositeNetworkData.icon; + document.getElementById('fromName').textContent = oppositeNetworkData.name; + } else if (isInitial) { + // Initial setup + if (!currentFromNetwork) { + currentFromNetwork = networkKey; + document.getElementById('fromIcon').src = network.icon; + document.getElementById('fromName').textContent = network.name; + + // Automatically set the opposite network as "To" + currentToNetwork = oppositeNetwork; + document.getElementById('toIcon').src = oppositeNetworkData.icon; + document.getElementById('toName').textContent = oppositeNetworkData.name; + } + } + + if (!isInitial) { + closeNetworkModal(); + updateBalances(); + validateBridgeButton(); + } +} + +async function swapDirection() { + const temp = currentFromNetwork; + currentFromNetwork = currentToNetwork; + currentToNetwork = temp; + + const fromNetwork = NETWORKS[currentFromNetwork]; + const toNetwork = NETWORKS[currentToNetwork]; + + document.getElementById('fromIcon').src = fromNetwork.icon; + document.getElementById('fromName').textContent = fromNetwork.name; + document.getElementById('toIcon').src = toNetwork.icon; + document.getElementById('toName').textContent = toNetwork.name; + + // Auto-switch network in wallet + if (account) { + try { + await switchNetwork(fromNetwork.chainId); + } catch (error) { + // User cancelled or network switch failed - continue anyway + } + } + + updateBalances(); +} + +// Provider cache with periodic refresh to avoid stale data +const providerCache = {}; +const PROVIDER_REFRESH_MS = 30000; // Refresh providers every 30 seconds +let lastProviderRefresh = 0; + +function getProvider(rpcUrl) { + const now = Date.now(); + // Clear cache periodically to avoid stale connections + if (now - lastProviderRefresh > PROVIDER_REFRESH_MS) { + Object.keys(providerCache).forEach(key => delete providerCache[key]); + lastProviderRefresh = now; + } + if (!providerCache[rpcUrl]) { + providerCache[rpcUrl] = new ethers.providers.JsonRpcProvider(rpcUrl); + } + return providerCache[rpcUrl]; +} + +// Balance Management - optimized with parallel fetches +async function updateBalances() { + if (!account || !currentFromNetwork || !currentToNetwork) return; + + try { + const fromNetwork = NETWORKS[currentFromNetwork]; + const toNetwork = NETWORKS[currentToNetwork]; + + const fromProvider = getProvider(fromNetwork.rpcUrl); + const toProvider = getProvider(toNetwork.rpcUrl); + + // Prepare all fetch promises - use 'latest' block tag to avoid caching + const fetchFromBalance = fromNetwork.tokenType === 'Native' + ? fromProvider.getBalance(account, 'latest') + : new ethers.Contract(fromNetwork.tokenAddress, ['function balanceOf(address) view returns (uint256)'], fromProvider).balanceOf(account, {blockTag: 'latest'}); + + const fetchToBalance = toNetwork.tokenType === 'Native' + ? toProvider.getBalance(account, 'latest') + : new ethers.Contract(toNetwork.tokenAddress, ['function balanceOf(address) view returns (uint256)'], toProvider).balanceOf(account, {blockTag: 'latest'}); + + const fetchLiquidity = toNetwork.tokenType === 'Native' + ? toProvider.getBalance(toNetwork.bridgeAddress, 'latest') + : new ethers.Contract(toNetwork.tokenAddress, ['function balanceOf(address) view returns (uint256)'], toProvider).balanceOf(toNetwork.bridgeAddress, {blockTag: 'latest'}); + + // Run all fetches in parallel + const [fromBal, toBal, liquidity] = await Promise.all([fetchFromBalance, fetchToBalance, fetchLiquidity]); + + fromBalance = fromBal; + toBalance = toBal; + bridgeLiquidity = liquidity; + + // Update UI + const fromBalFormatted = parseFloat(ethers.utils.formatEther(fromBalance)).toFixed(4); + const toBalFormatted = parseFloat(ethers.utils.formatEther(toBalance)).toFixed(4); + const liqFormatted = parseFloat(ethers.utils.formatEther(liquidity)).toFixed(2); + + document.getElementById('fromBalance').textContent = `Balance: ${fromBalFormatted} POL`; + document.getElementById('toBalance').textContent = `Balance: ${toBalFormatted} POL`; + document.getElementById('bridgeLiquidity').textContent = `Liquidity: ${liqFormatted} POL`; + + console.log(`[Balance] From: ${fromBalFormatted}, To: ${toBalFormatted}, Liq: ${liqFormatted}`); + + validateBridgeButton(); + + } catch (error) { + console.error('Balance update error:', error); + document.getElementById('fromBalance').textContent = 'Balance: Error'; + document.getElementById('toBalance').textContent = 'Balance: Error'; + document.getElementById('bridgeLiquidity').textContent = 'Liquidity: Error'; + } +} + +function setMaxAmount() { + if (!fromBalance || !currentFromNetwork) { + showStatus('Please wait for balance to load', 'info'); + return; + } + + const fromNetwork = NETWORKS[currentFromNetwork]; + let maxAmount; + + if (fromNetwork.tokenType === 'Native') { + // Reserve some for gas (0.01 POL) + const gasReserve = ethers.utils.parseEther('0.01'); + maxAmount = fromBalance.sub(gasReserve); + + if (maxAmount.lt(0)) { + maxAmount = ethers.BigNumber.from(0); + } + } else { + // For ERC20, can use full balance + maxAmount = fromBalance; + } + + const formattedAmount = ethers.utils.formatEther(maxAmount); + // Clean up floating point precision issues + const cleanAmount = parseFloat(formattedAmount).toString(); + document.getElementById('amountInput').value = cleanAmount; + + updateBridgeSummary(); + validateBridgeButton(); +} + +// Bridge Summary +function updateBridgeSummary() { + const amount = document.getElementById('amountInput').value; + if (amount && parseFloat(amount) > 0) { + document.getElementById('bridgeSummary').style.display = 'block'; + document.getElementById('receiveAmount').textContent = `${amount} POL`; + } else { + document.getElementById('bridgeSummary').style.display = 'none'; + } +} + +// Bridge Validation +function validateBridgeButton() { + const amount = document.getElementById('amountInput').value; + const recipient = document.getElementById('recipientInput').value || account; + const bridgeBtn = document.getElementById('bridgeBtn'); + + if (!account) { + bridgeBtn.disabled = true; + bridgeBtn.textContent = 'Connect Wallet to Bridge'; + } else if (!currentFromNetwork || !currentToNetwork) { + bridgeBtn.disabled = true; + bridgeBtn.textContent = 'Select Networks'; + } else if (!amount || parseFloat(amount) <= 0) { + bridgeBtn.disabled = true; + bridgeBtn.textContent = 'Enter Amount'; + } else if (!recipient || !ethers.utils.isAddress(recipient)) { + bridgeBtn.disabled = true; + bridgeBtn.textContent = 'Invalid Recipient'; + } else if (bridgeLiquidity && ethers.utils.parseEther(amount).gt(bridgeLiquidity)) { + bridgeBtn.disabled = true; + bridgeBtn.textContent = '⚠️ Insufficient Liquidity'; + } else { + bridgeBtn.disabled = false; + bridgeBtn.textContent = '⚑ Bridge with Permit'; + } +} + +// EIP-2612 Permit Signature +async function getPermitSignature(tokenAddress, spender, value, deadline) { + try { + const token = new ethers.Contract( + tokenAddress, + [ + 'function name() view returns (string)', + 'function nonces(address) view returns (uint256)', + ], + signer + ); + + const [name, nonce] = await Promise.all([ + token.name(), + token.nonces(account) + ]); + + const chainId = await signer.getChainId(); + + // EIP-712 domain + const domain = { + name: name, + version: '1', + chainId: chainId, + verifyingContract: tokenAddress + }; + + // EIP-712 types + const types = { + Permit: [ + { name: 'owner', type: 'address' }, + { name: 'spender', type: 'address' }, + { name: 'value', type: 'uint256' }, + { name: 'nonce', type: 'uint256' }, + { name: 'deadline', type: 'uint256' } + ] + }; + + // Message + const message = { + owner: account, + spender: spender, + value: value.toString(), + nonce: nonce.toString(), + deadline: deadline + }; + + // Sign + const signature = await signer._signTypedData(domain, types, message); + const sig = ethers.utils.splitSignature(signature); + + return { v: sig.v, r: sig.r, s: sig.s }; + + } catch (error) { + console.error('Permit signature error:', error); + throw error; + } +} + +// Bridge with Permit +async function initiateBridge() { + try { + const amount = document.getElementById('amountInput').value; + const recipient = document.getElementById('recipientInput').value || account; + const fromNetwork = NETWORKS[currentFromNetwork]; + const toNetwork = NETWORKS[currentToNetwork]; + + if (!amount || !recipient) { + showStatus('Please fill in all fields', 'error'); + return; + } + + // Disable button + document.getElementById('bridgeBtn').disabled = true; + showStatus('Preparing bridge transaction...', 'info'); + + const amountWei = ethers.utils.parseEther(amount); + + // Check if we're on the correct network + const chainId = await signer.getChainId(); + if (chainId !== fromNetwork.chainId) { + await switchNetwork(fromNetwork.chainId); + } + + let permitData = '0x'; + + // Get permit signature for ERC20 tokens + if (fromNetwork.tokenType === 'ERC20') { + try { + showStatus('πŸ“ Please sign the permit message...', 'info'); + const deadline = Math.floor(Date.now() / 1000) + 3600; // 1 hour + + const { v, r, s } = await getPermitSignature( + fromNetwork.tokenAddress, + fromNetwork.bridgeAddress, + amountWei, + deadline + ); + + // Encode permit data + permitData = ethers.utils.defaultAbiCoder.encode( + ['address', 'address', 'uint256', 'uint256', 'uint8', 'bytes32', 'bytes32'], + [account, fromNetwork.bridgeAddress, amountWei, deadline, v, r, s] + ); + } catch (error) { + // Permit failed, falling back to approval + showStatus('Getting token approval...', 'info'); + + const token = new ethers.Contract( + fromNetwork.tokenAddress, + ['function approve(address, uint256)'], + signer + ); + + const approveTx = await token.approve(fromNetwork.bridgeAddress, amountWei); + await approveTx.wait(); + } + } + + // Bridge transaction + showStatus('πŸš€ Submitting bridge transaction...', 'info'); + + const bridge = new ethers.Contract( + fromNetwork.bridgeAddress, + [ + 'function bridgeAsset(address, uint256, uint256, address, bytes) payable returns (bytes32)' + ], + signer + ); + + const txOptions = fromNetwork.tokenType === 'Native' ? + { value: amountWei } : {}; + + const tx = await bridge.bridgeAsset( + fromNetwork.tokenAddress, + amountWei, + toNetwork.chainId, + recipient, + permitData, + txOptions + ); + + showStatus('⏳ Waiting for confirmation...', 'info'); + const receipt = await tx.wait(); + + // Extract bridgeId from BridgeInitiated event + let bridgeId = null; + if (receipt.logs && receipt.logs.length > 0) { + // BridgeInitiated event signature + const bridgeInitiatedTopic = '0xa43a2e0bb4454dc2f20f4a34be7549f0e1b00e4f5e88805c729900e40471a0cb'; + const bridgeLog = receipt.logs.find(log => log.topics[0] === bridgeInitiatedTopic); + if (bridgeLog && bridgeLog.topics.length >= 2) { + bridgeId = bridgeLog.topics[1]; // bridgeId is the first indexed parameter + } + } + + // Save to history + saveTransaction({ + hash: receipt.transactionHash, + bridgeId: bridgeId, + from: fromNetwork.name, + to: toNetwork.name, + amount: amount, + timestamp: Date.now(), + status: 'Pending' // Initial status, will be updated by RPC query + }); + + showStatus( + `βœ… Bridge successful!
Transaction: ${receipt.transactionHash.substring(0, 10)}...`, + 'success' + ); + + // Clear form + document.getElementById('amountInput').value = ''; + updateBridgeSummary(); + updateBalances(); + loadHistory(); + + } catch (error) { + console.error('Bridge error:', error); + showStatus('❌ Bridge failed: ' + error.message, 'error'); + } finally { + validateBridgeButton(); + } +} + +async function switchNetwork(chainId) { + try { + await window.ethereum.request({ + method: 'wallet_switchEthereumChain', + params: [{ chainId: '0x' + chainId.toString(16) }], + }); + } catch (error) { + if (error.code === 4902) { + // Chain not added, add it + if (chainId === CONFIG.STAVANGER_CHAIN_ID) { + await window.ethereum.request({ + method: 'wallet_addEthereumChain', + params: [{ + chainId: '0x' + chainId.toString(16), + chainName: 'Stavanger', + nativeCurrency: { + name: 'POL', + symbol: 'POL', + decimals: 18 + }, + rpcUrls: [CONFIG.STAVANGER_RPC], + blockExplorerUrls: ['https://explorer.stavanger.gateway.fm'] + }] + }); + } + } else { + throw error; + } + } +} + +// Transaction History +function saveTransaction(tx) { + const history = JSON.parse(localStorage.getItem('bridgeHistory') || '[]'); + history.unshift(tx); + localStorage.setItem('bridgeHistory', JSON.stringify(history.slice(0, 50))); // Keep last 50 +} + +// Query bridge status from appchain RPC +async function queryBridgeStatus(bridgeId) { + if (!bridgeId) return null; + + try { + const response = await fetch(CONFIG.APPCHAIN_RPC, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + jsonrpc: '2.0', + method: 'getBridgeStatus', + params: [{ bridgeId }], + id: 1 + }) + }); + + const data = await response.json(); + if (data.result) { + return { + status: data.result.status, + claimTxHash: data.result.claimTxHash || null + }; + } + } catch (error) { + // Silently fail - appchain might not be running + } + + return null; +} + +async function loadHistory() { + const history = JSON.parse(localStorage.getItem('bridgeHistory') || '[]'); + const historyList = document.getElementById('historyList'); + + if (history.length === 0) { + historyList.innerHTML = ` +
+ + + + +

No transactions yet

+ Your bridge transactions will appear here +
+ `; + return; + } + + // Query real status for each transaction + const updatedHistory = await Promise.all(history.map(async (tx) => { + if (tx.bridgeId && tx.status !== 'Completed') { + const rpcResult = await queryBridgeStatus(tx.bridgeId); + if (rpcResult) { + tx.status = rpcResult.status; + if (rpcResult.claimTxHash) { + tx.claimTxHash = rpcResult.claimTxHash; + } + } + } + return tx; + })); + + // Update localStorage with real statuses + localStorage.setItem('bridgeHistory', JSON.stringify(updatedHistory)); + + // Auto-refresh: start interval if there are pending transactions, stop if all completed + const hasPending = updatedHistory.some(tx => tx.status !== 'Completed'); + if (hasPending && !historyRefreshInterval) { + historyRefreshInterval = setInterval(loadHistory, HISTORY_REFRESH_MS); + } else if (!hasPending && historyRefreshInterval) { + clearInterval(historyRefreshInterval); + historyRefreshInterval = null; + } + + // Group transactions by status + const pending = updatedHistory.filter(tx => tx.status === 'Pending'); + const confirmed = updatedHistory.filter(tx => tx.status === 'Confirmed'); + const completed = updatedHistory.filter(tx => tx.status === 'Completed'); + + const renderTx = (tx) => { + const sourceExplorerUrl = tx.from === 'Sepolia' + ? `https://sepolia.etherscan.io/tx/${tx.hash}` + : `https://explorer.stavanger.gateway.fm/tx/${tx.hash}`; + + const destExplorerBase = tx.from === 'Sepolia' + ? 'https://explorer.stavanger.gateway.fm/tx/' + : 'https://sepolia.etherscan.io/tx/'; + + let txLinksHtml = `Source Tx`; + + if (tx.claimTxHash) { + txLinksHtml += ` | Claim Tx`; + } + + return ` +
+
+
+ + + +
+
+

${tx.amount} POL - ${tx.from} β†’ ${tx.to}

+

${new Date(tx.timestamp).toLocaleString()}

+ ${tx.bridgeId ? `

Bridge ID: ${tx.bridgeId.substring(0, 10)}...

` : ''} + +
+
+
+ ${tx.status} +
+
+ `; + }; + + const renderSection = (title, icon, txs, colorClass) => { + if (txs.length === 0) return ''; + return ` +
+
+ ${icon} + ${title} (${txs.length}) +
+ ${txs.map(renderTx).join('')} +
+ `; + }; + + historyList.innerHTML = + renderSection('Pending', '', pending, 'pending') + + renderSection('Bridged', '', confirmed, 'confirmed') + + renderSection('Claimed', '', completed, 'completed'); +} + +// Clear History +function clearHistory() { + localStorage.removeItem('bridgeHistory'); + if (historyRefreshInterval) { + clearInterval(historyRefreshInterval); + historyRefreshInterval = null; + } + loadHistory(); +} + +// Faucet - Mint test POL tokens on Sepolia +async function mintTestPOL() { + try { + if (!account) { + showFaucetStatus('Please connect your wallet first', 'error'); + return; + } + + const faucetBtn = document.getElementById('faucetBtn'); + faucetBtn.disabled = true; + faucetBtn.textContent = 'Minting...'; + + // Check if on Sepolia and switch if needed + const chainId = await signer.getChainId(); + if (chainId !== CONFIG.SEPOLIA_CHAIN_ID) { + showFaucetStatus('Switching to Sepolia...', 'info'); + await switchNetwork(CONFIG.SEPOLIA_CHAIN_ID); + // Refresh provider and signer after network switch + provider = new ethers.providers.Web3Provider(window.ethereum); + signer = provider.getSigner(); + } + + showFaucetStatus('Minting 10 test POL tokens...', 'info'); + + const polToken = new ethers.Contract( + CONFIG.POL_TOKEN_SEPOLIA, + ['function mint(address to, uint256 amount)'], + signer + ); + + const mintAmount = ethers.utils.parseEther('10'); // 10 POL + const tx = await polToken.mint(account, mintAmount); + + showFaucetStatus('Waiting for confirmation...', 'info'); + await tx.wait(); + + showFaucetStatus('Successfully minted 10 test POL!', 'success'); + updateBalances(); + updateFaucetBalance(); + + } catch (error) { + console.error('Faucet error:', error); + showFaucetStatus('Faucet failed: ' + error.message, 'error'); + } finally { + const faucetBtn = document.getElementById('faucetBtn'); + faucetBtn.disabled = false; + faucetBtn.textContent = 'Get 10 Test POL'; + } +} + +// Show status in faucet tab +function showFaucetStatus(message, type) { + const statusDiv = document.getElementById('faucetStatus'); + statusDiv.className = `faucet-status ${type}`; + statusDiv.textContent = message; + statusDiv.style.display = 'block'; + + if (type === 'success') { + setTimeout(() => { + statusDiv.style.display = 'none'; + }, 5000); + } +} + +// Update faucet balance display +async function updateFaucetBalance() { + const faucetBalanceEl = document.getElementById('faucetBalance'); + if (!account) { + faucetBalanceEl.textContent = 'Connect wallet'; + return; + } + + try { + const sepoliaProvider = getProvider(CONFIG.SEPOLIA_RPC); + const polToken = new ethers.Contract( + CONFIG.POL_TOKEN_SEPOLIA, + ['function balanceOf(address) view returns (uint256)'], + sepoliaProvider + ); + const balance = await polToken.balanceOf(account); + const formatted = parseFloat(ethers.utils.formatEther(balance)).toFixed(4); + faucetBalanceEl.textContent = `${formatted} POL`; + } catch (error) { + faucetBalanceEl.textContent = 'Error loading'; + } +} + +// Status Messages +function showStatus(message, type) { + const statusDiv = document.getElementById('statusMessage'); + statusDiv.className = `status-message ${type}`; + statusDiv.innerHTML = message; + statusDiv.style.display = 'block'; + + if (type === 'success') { + setTimeout(() => { + statusDiv.style.display = 'none'; + }, 10000); + } +} diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000..48a4c70 --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,236 @@ + + + + + + Pelagos Bridge | Cross-Chain Bridge + + + + + + + + + + + + +
+ +
+
+
+ +
+
+ From + Balance: -- +
+
+
+ Network + Select Network + + + +
+
+ POL + POL +
+
+
+ + +
+
+ + +
+ +
+ + +
+
+ To + Balance: -- + Liquidity: -- +
+
+
+ Network + Select Network + + + +
+
+ POL + POL +
+
+
+ To: + Connect wallet + + +
+
+ + + + + + + + + + +
+
+
+ + +
+
+
+
+ + + +
+

Test Token Faucet

+

Get free test POL tokens on Sepolia to try the bridge

+
+
+
+
+ Network + Sepolia (Ethereum Testnet) +
+
+ Token + POL (Test Token) +
+
+ Amount + 10 POL per request +
+
+ Your Balance + Connect wallet +
+
+ + +
+ + + + + Test tokens have no real value. Use them to test the bridge functionality. +
+
+
+
+ + +
+
+
+

Transaction History

+ +
+
+
+ + + + +

No transactions yet

+ Your bridge transactions will appear here +
+
+
+
+
+ + + + + + + diff --git a/frontend/styles.css b/frontend/styles.css new file mode 100644 index 0000000..c81f8c3 --- /dev/null +++ b/frontend/styles.css @@ -0,0 +1,1081 @@ +/* Root Variables - Pelagos Design System */ +:root { + /* Palette */ + --blue-600: #1675F3; + --blue-400: #2F84F1; + --pearl: #F1F1EF; + --mint: #8CF2C3; + --yellow: #F3E96D; + --grey: #566874; + --dark: #011439; + + /* RGB helpers for translucency */ + --primary-rgb: 22, 117, 243; + --dark-rgb: 1, 20, 57; + + /* Typography */ + --font-body: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + --font-heading: var(--font-body); + + /* Semantic tokens */ + --primary: var(--blue-600); + --primary-hover: var(--blue-400); + --success: var(--mint); + --error: #ef4444; + + --bg: var(--pearl); + --surface: #ffffff; + --surface-2: rgba(var(--dark-rgb), 0.03); + + --text-primary: var(--dark); + --text-secondary: var(--grey); + --text-light: rgba(var(--dark-rgb), 0.55); + + --border: rgba(var(--dark-rgb), 0.12); + --shadow: rgba(0, 0, 0, 0.08); + --shadow-lg: rgba(0, 0, 0, 0.14); + + /* Warning styling (from design notes) */ + --warning: #E7A003; + --warning-bg: #FFF6DF; +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +button, +input, +select, +textarea { + font: inherit; +} + +body { + font-family: var(--font-body); + background: var(--bg); + min-height: 100vh; + color: var(--text-primary); + line-height: 1.5; +} + +h1, h2, h3, h4, +.brand-name { + font-family: var(--font-body); + line-height: 1; + letter-spacing: -0.02em; +} + +h1 { font-size: 54px; font-weight: 700; } +h2 { font-size: 40px; font-weight: 700; } +h3 { font-size: 24px; font-weight: 700; } +h4 { font-size: 16px; font-weight: 700; } + +/* Navbar */ +.navbar { + background: rgba(241, 241, 239, 0.92); + backdrop-filter: blur(10px); + border-bottom: 1px solid var(--border); + position: sticky; + top: 0; + z-index: 100; + box-shadow: 0 2px 8px var(--shadow); +} + +.nav-container { + max-width: 1200px; + margin: 0 auto; + padding: 1rem 2rem; + display: flex; + align-items: center; + justify-content: space-between; +} + +.nav-brand { + flex: 1 1 0; + display: flex; + align-items: center; + justify-content: flex-start; + gap: 0.75rem; + font-weight: 700; + font-size: 1.25rem; +} + +.logo { + font-size: 1.5rem; +} + +.logo-svg { + height: 32px; + width: auto; + margin-right: 0.5rem; +} + +.brand-name { + color: var(--text-primary); + font-weight: 600; +} + +.badge-beta { + background: var(--yellow); + color: var(--dark); + padding: 0.125rem 0.5rem; + border-radius: 0.25rem; + font-size: 0.75rem; + font-weight: 600; +} + +.nav-links { + flex: 1 1 0; + display: flex; + justify-content: center; + gap: 0.5rem; +} + +.nav-wallet { + flex: 1 1 0; + display: flex; + justify-content: flex-end; +} + +.nav-link { + background: none; + border: none; + padding: 0.5rem 1rem; + cursor: pointer; + color: var(--text-secondary); + font-weight: 500; + transition: all 0.2s; + border-radius: 0.5rem; +} + +.nav-link:hover { + color: var(--primary); + background: rgba(var(--primary-rgb), 0.08); +} + +.nav-link.active { + color: var(--primary); + background: rgba(var(--primary-rgb), 0.10); +} + +.btn-connect { + background: var(--primary); + color: #fff; + border: none; + padding: 0.75rem 1.5rem; + border-radius: 0.75rem; + font-weight: 600; + cursor: pointer; + transition: all 0.3s; + box-shadow: 0 4px 12px rgba(var(--primary-rgb), 0.28); +} + +.btn-connect:hover { + transform: translateY(-2px); + background: var(--primary-hover); + box-shadow: 0 6px 16px rgba(var(--primary-rgb), 0.34); +} + +.btn-connect.connected { + background: var(--mint); + color: var(--dark); +} + +/* Container */ +.container { + max-width: 800px; + margin: 1rem auto; + padding: 0 1rem; +} + +/* Tab Content */ +.tab-content { + display: none; +} + +.tab-content.active { + display: block; +} + +/* Bridge Card */ +.bridge-card, .history-card, .faq-card { + background: var(--surface); + border-radius: 2rem; + padding: 2rem; + box-shadow: none; + border: 1px solid var(--border); +} + +.card-header { + text-align: center; + margin-bottom: 1rem; +} + +.card-header h1 { + font-size: 54px; + margin-bottom: 0.5rem; + color: var(--text-primary); +} + +.subtitle { + color: var(--text-secondary); + font-size: 1rem; +} + +/* Transfer Section */ +.transfer-section { + background: var(--surface-2); + border-radius: 1.25rem; + padding: 1.5rem; + margin-bottom: 0; + border: 1px solid var(--border); +} + +.swap-direction + .transfer-section { + margin-bottom: 1rem; +} + +.section-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 1rem; + font-weight: 500; + color: var(--text-secondary); + font-size: 0.875rem; +} + +.balance { + color: var(--text-light); +} + +.liquidity { + color: #10b981; + font-size: 0.75rem; +} + +.input-group { + display: grid; + grid-template-columns: 1fr; + gap: 0.75rem; + margin-bottom: 0.75rem; +} + +.network-selector, .token-selector { + display: flex; + align-items: center; + gap: 0.75rem; + padding: 1rem 1.25rem; + background: rgba(var(--dark-rgb), 0.02); + border: 0; + border-radius: 1rem; + cursor: pointer; + transition: all 0.2s; +} + +.token-selector, +#fromToken, +#toToken { + display: none !important; + visibility: hidden !important; + opacity: 0 !important; + width: 0 !important; + height: 0 !important; + overflow: hidden !important; +} + +.network-selector:hover { + background: rgba(var(--primary-rgb), 0.06); +} + +.network-icon, .token-icon { + width: 32px; + height: 32px; + border-radius: 50%; +} + +/* Default (before network selection) shows as a grey circle */ +.network-icon { + background: rgba(var(--dark-rgb), 0.10); + border: 1px solid var(--border); +} + +.network-name { + flex: 1; + font-weight: 600; +} + +.chevron { + color: var(--text-light); +} + +.token-selector { + cursor: default; +} + +.amount-input-wrapper { + position: relative; +} + +.amount-input { + width: 100%; + padding: 1rem 1.25rem; + padding-right: 6rem; + background: rgba(var(--dark-rgb), 0.02); + border: 0; + border-radius: 1rem; + font-size: 1.5rem; + font-weight: 400; + transition: all 0.2s; +} + +/* Destination address display */ +.destination-address { + display: flex; + align-items: center; + gap: 0.5rem; + margin-top: 0.75rem; + padding: 0.5rem 0.75rem; + background: rgba(var(--primary-rgb), 0.06); + border-radius: 0.5rem; + font-size: 0.8rem; +} + +.destination-label { + color: var(--text-secondary); +} + +.destination-value { + flex: 1; + color: var(--text-primary); + font-weight: 500; + font-family: var(--font-body); +} + +.recipient-input-inline { + flex: 1; + background: transparent; + border: none; + outline: none; + font-size: 0.8rem; + font-weight: 500; + font-family: var(--font-body); + color: var(--text-primary); + min-width: 0; +} + +.recipient-input-inline::placeholder { + color: var(--text-light); +} + +.recipient-input-inline.error { + color: var(--error); +} + +.recipient-input-inline.error::placeholder { + color: var(--error); +} + +.btn-change-recipient { + background: none; + border: none; + color: var(--primary); + font-size: 0.75rem; + font-weight: 500; + cursor: pointer; + padding: 0; + white-space: nowrap; +} + +.btn-change-recipient:hover { + text-decoration: underline; +} + +.amount-input:focus { + outline: none; + border-color: rgba(var(--primary-rgb), 0.45); + background: rgba(var(--primary-rgb), 0.06); +} + +.btn-max { + position: absolute; + right: 0.75rem; + top: 50%; + transform: translateY(-50%); + background: rgba(var(--primary-rgb), 0.10); + color: var(--primary); + border: none; + padding: 0.5rem 1rem; + border-radius: 0.5rem; + font-weight: 600; + font-size: 0.875rem; + cursor: pointer; + transition: all 0.2s; +} + +.btn-max:hover { + background: rgba(var(--primary-rgb), 0.18); +} + +/* Faucet Button */ +.btn-faucet { + background: linear-gradient(135deg, #10b981, #059669); + color: white; + border: none; + padding: 0.25rem 0.75rem; + border-radius: 0.375rem; + font-size: 0.75rem; + font-weight: 600; + cursor: pointer; + transition: all 0.2s; + margin-left: auto; +} + +.btn-faucet:hover { + background: linear-gradient(135deg, #059669, #047857); + transform: scale(1.05); +} + +.btn-faucet:disabled { + opacity: 0.6; + cursor: not-allowed; + transform: none; +} + +/* Faucet Tab */ +.faucet-card { + background: var(--surface); + border-radius: 1.5rem; + box-shadow: 0 10px 40px var(--shadow-lg); + max-width: 480px; + margin: 2rem auto; + overflow: hidden; +} + +.faucet-header { + background: linear-gradient(135deg, #10b981, #059669); + color: white; + padding: 2rem; + text-align: center; +} + +.faucet-icon { + margin-bottom: 1rem; +} + +.faucet-icon svg { + stroke: white; +} + +.faucet-header h2 { + font-size: 1.5rem; + margin-bottom: 0.5rem; +} + +.faucet-header p { + opacity: 0.9; + font-size: 0.9rem; +} + +.faucet-body { + padding: 1.5rem; +} + +.faucet-info { + background: #f8fafc; + border-radius: 0.75rem; + padding: 1rem; + margin-bottom: 1.5rem; +} + +.faucet-info-row { + display: flex; + justify-content: space-between; + padding: 0.5rem 0; + border-bottom: 1px solid #e2e8f0; +} + +.faucet-info-row:last-child { + border-bottom: none; +} + +.faucet-info-row span:first-child { + color: var(--text-secondary); + font-size: 0.875rem; +} + +.faucet-value { + font-weight: 600; + color: var(--text-primary); + font-size: 0.875rem; +} + +.btn-faucet-main { + width: 100%; + background: linear-gradient(135deg, #10b981, #059669); + color: white; + border: none; + padding: 1rem; + border-radius: 0.75rem; + font-size: 1rem; + font-weight: 600; + cursor: pointer; + transition: all 0.2s; +} + +.btn-faucet-main:hover { + background: linear-gradient(135deg, #059669, #047857); + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(16, 185, 129, 0.3); +} + +.btn-faucet-main:disabled { + opacity: 0.6; + cursor: not-allowed; + transform: none; + box-shadow: none; +} + +.faucet-status { + margin-top: 1rem; + padding: 0.75rem; + border-radius: 0.5rem; + font-size: 0.875rem; + text-align: center; +} + +.faucet-status.success { + background: #d1fae5; + color: #065f46; +} + +.faucet-status.error { + background: #fee2e2; + color: #991b1b; +} + +.faucet-status.info { + background: #dbeafe; + color: #1e40af; +} + +.faucet-note { + display: flex; + align-items: flex-start; + gap: 0.5rem; + margin-top: 1.5rem; + padding: 0.75rem; + background: #fef3c7; + border-radius: 0.5rem; + font-size: 0.75rem; + color: #92400e; +} + +.faucet-note svg { + flex-shrink: 0; + stroke: #92400e; +} + +/* Swap Direction */ +.swap-direction { + display: flex; + justify-content: center; + margin: -0.5rem 0; + position: relative; + z-index: 10; +} + +.btn-swap { + background: white; + border: 2px solid var(--border); + border-radius: 0.75rem; + width: 40px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + transition: all 0.3s; + color: var(--text-secondary); +} + +.btn-swap:hover { + transform: rotate(180deg); + border-color: var(--primary); + color: var(--primary); +} + +/* Bridge Summary */ +.bridge-summary { + background: #f8fafc; + border-radius: 0.75rem; + padding: 0.75rem; + margin: 0.75rem 0; +} + +.summary-row { + display: flex; + justify-content: space-between; + margin-bottom: 0.75rem; + font-size: 0.875rem; +} + +.summary-row:last-child { + margin-bottom: 0; +} + +.summary-row span:first-child { + color: var(--text-secondary); +} + +.summary-row span:last-child { + font-weight: 600; +} + +/* Bridge Button */ +.btn-bridge { + width: 100%; + padding: 1.25rem; + background: var(--primary); + color: white; + border: none; + border-radius: 1rem; + font-size: 1.125rem; + font-weight: 600; + cursor: pointer; + transition: all 0.2s; + box-shadow: none; +} + +.btn-bridge:hover:not(:disabled) { + background: var(--primary-hover); +} + +.btn-bridge:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +/* Status Message */ +.status-message { + margin-top: 1rem; + padding: 1rem; + border-radius: 0.75rem; + font-size: 0.875rem; +} + +.status-message.info { + background: #dbeafe; + color: #1e40af; +} + +.status-message.success { + background: #d1fae5; + color: #065f46; +} + +.status-message.error { + background: #fee2e2; + color: #991b1b; +} + +/* Info Box */ +.info-box { + margin-top: 1rem; + padding: 0.875rem; + background: rgba(var(--primary-rgb), 0.06); + border-left: 4px solid var(--primary); + border-radius: 0.5rem; +} + +.info-header { + display: flex; + align-items: center; + gap: 0.5rem; + font-weight: 600; + margin-bottom: 0.5rem; + color: var(--primary); +} + +.info-icon { + color: var(--primary); +} + +.info-box p { + font-size: 0.875rem; + color: var(--text-secondary); + line-height: 1.6; +} + +/* History */ +.history-header { + display: flex; + justify-content: space-between; + align-items: center; +} + +.history-header h2 { + font-size: 1.25rem; + color: var(--text-primary); +} + +.btn-clear-history { + background: rgba(239, 68, 68, 0.1); + color: #ef4444; + border: none; + padding: 0.5rem 1rem; + border-radius: 0.5rem; + font-weight: 600; + font-size: 0.875rem; + cursor: pointer; + transition: all 0.2s; +} + +.btn-clear-history:hover { + background: rgba(239, 68, 68, 0.2); +} + +.history-list { + margin-top: 1.5rem; +} + +.empty-state { + text-align: center; + padding: 3rem 1rem; + color: var(--text-light); +} + +.empty-state svg { + margin-bottom: 1rem; +} + +.empty-state p { + font-size: 1.125rem; + font-weight: 600; + color: var(--text-secondary); + margin-bottom: 0.5rem; +} + +.history-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 1rem; + background: #f8fafc; + border-radius: 0.75rem; + margin-bottom: 0.75rem; + transition: all 0.2s; +} + +.history-item:hover { + background: rgba(var(--primary-rgb), 0.06); + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(var(--primary-rgb), 0.10); +} + +.history-info { + display: flex; + align-items: center; + gap: 1rem; +} + +.history-icon { + width: 40px; + height: 40px; + border-radius: 50%; + background: rgba(var(--primary-rgb), 0.10); + display: flex; + align-items: center; + justify-content: center; + color: var(--primary); +} + +.history-details h4 { + font-size: 0.875rem; + margin-bottom: 0.25rem; +} + +.history-details p { + font-size: 0.75rem; + color: var(--text-light); +} + +.history-status { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.status-badge { + padding: 0.25rem 0.75rem; + border-radius: 1rem; + font-size: 0.75rem; + font-weight: 600; +} + +.status-badge.pending { + background: rgba(245, 158, 11, 0.1); + color: #f59e0b; +} + +.status-badge.confirmed { + background: rgba(59, 130, 246, 0.1); + color: #3b82f6; +} + +.status-badge.completed { + background: rgba(16, 185, 129, 0.1); + color: #10b981; +} + +.status-badge.failed { + background: rgba(239, 68, 68, 0.1); + color: #ef4444; +} + +.bridge-id { + font-size: 0.75rem; + color: #6b7280; + margin-top: 0.25rem; +} + +/* History Sections */ +.history-section { + margin-bottom: 1.5rem; +} + +.history-section-header { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.75rem 1rem; + border-radius: 0.5rem; + margin-bottom: 0.75rem; + font-weight: 600; + font-size: 0.875rem; +} + +.history-section-header.pending { + background: rgba(245, 158, 11, 0.1); + color: #f59e0b; +} + +.history-section-header.confirmed { + background: rgba(59, 130, 246, 0.1); + color: #3b82f6; +} + +.history-section-header.completed { + background: rgba(16, 185, 129, 0.1); + color: #10b981; +} + +/* FAQ */ +.faq-list { + margin-top: 1.5rem; +} + +.faq-item { + padding: 1.5rem; + background: #f8fafc; + border-radius: 0.75rem; + margin-bottom: 1rem; +} + +.faq-item h3 { + font-size: 1rem; + margin-bottom: 0.75rem; + color: var(--text-primary); +} + +.faq-item p { + font-size: 0.875rem; + color: var(--text-secondary); + line-height: 1.6; +} + +/* Network Modal */ +.modal { + display: none; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 1000; +} + +.modal.active { + display: block; +} + +.modal-backdrop { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + backdrop-filter: blur(4px); +} + +.modal-content { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background: white; + border-radius: 1.5rem; + padding: 2rem; + max-width: 400px; + width: 90%; + max-height: 80vh; + overflow-y: auto; + box-shadow: 0 20px 60px var(--shadow-lg); +} + +.modal-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 1.5rem; +} + +.modal-header h3 { + font-size: 1.25rem; +} + +.modal-close { + background: none; + border: none; + font-size: 2rem; + line-height: 1; + cursor: pointer; + color: var(--text-light); + padding: 0; + width: 32px; + height: 32px; + display: flex; + align-items: center; + justify-content: center; +} + +.network-list { + display: flex; + flex-direction: column; + gap: 0.75rem; +} + +.network-option { + display: flex; + align-items: center; + gap: 1rem; + padding: 1rem; + background: #f8fafc; + border: 2px solid transparent; + border-radius: 0.75rem; + cursor: pointer; + transition: all 0.2s; + width: 100%; +} + +.network-option:hover { + background: rgba(var(--primary-rgb), 0.06); + border-color: var(--primary); +} + +.network-option.selected { + background: rgba(var(--primary-rgb), 0.10); + border-color: var(--primary); +} + +.network-option img { + width: 40px; + height: 40px; + border-radius: 50%; +} + +.network-option > div { + flex: 1; + text-align: left; +} + +.network-option-name { + font-weight: 600; + margin-bottom: 0.25rem; +} + +.network-option-desc { + font-size: 0.75rem; + color: var(--text-light); +} + +.check-icon { + color: var(--primary); + opacity: 0; +} + +.network-option.selected .check-icon { + opacity: 1; +} + +/* Responsive */ +@media (max-width: 768px) { + h1 { font-size: 40px; } + h2 { font-size: 32px; } + h3 { font-size: 20px; } + h4 { font-size: 16px; } + + .nav-container { + flex-wrap: wrap; + gap: 1rem; + } + + .nav-links { + order: 3; + flex: unset; + width: 100%; + justify-content: center; + } + + .nav-brand, .nav-wallet { + flex: unset; + } + + .bridge-card, .history-card, .faucet-card, .faq-card { + padding: 1.5rem; + } + + .input-group { + grid-template-columns: 1fr; + } +} + +/* Animations */ +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.fade-in { + animation: fadeIn 0.3s ease-out; +} + +@keyframes spin { + to { + transform: rotate(360deg); + } +} + +.loading { + animation: spin 1s linear infinite; +} diff --git a/generate_ext_txn.sh b/generate_ext_txn.sh deleted file mode 100755 index fc1cd01..0000000 --- a/generate_ext_txn.sh +++ /dev/null @@ -1,253 +0,0 @@ -#!/bin/bash - -# External Transaction Generation Script -# This script sends a transaction to the appchain that generates an external transaction to Sepolia -# Your teammate can use this to test the explorer's external transaction handling - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -# API endpoint -API_URL="${API_URL:-http://localhost:8080/rpc}" -CONTENT_TYPE="Content-Type: application/json" - -# Counter for request IDs -REQUEST_ID=1 - -# Retry settings for transaction status polling -STATUS_MAX_ATTEMPTS=10 -STATUS_SLEEP=2 - -# Function to print colored messages -print_header() { - echo -e "\n${BLUE}════════════════════════════════════════════════════════════════${NC}" - echo -e "${YELLOW}$1${NC}" - echo -e "${BLUE}════════════════════════════════════════════════════════════════${NC}" -} - -print_success() { - echo -e "${GREEN}βœ“ $1${NC}" -} - -print_error() { - echo -e "${RED}βœ— $1${NC}" -} - -print_info() { - echo -e "${BLUE}β„Ή $1${NC}" -} - -# Function to generate unique transaction hash -generate_tx_hash() { - payload="$(date +%s%N)$RANDOM" - - if command -v sha256sum >/dev/null 2>&1; then - hash=$(printf "%s" "$payload" | sha256sum | awk '{print $1}') - elif command -v shasum >/dev/null 2>&1; then - hash=$(printf "%s" "$payload" | shasum -a 256 | awk '{print $1}') - elif command -v openssl >/dev/null 2>&1; then - hash=$(printf "%s" "$payload" | openssl dgst -sha256 | awk '{print $NF}') - else - hash=$(printf "%s" "$payload" | xxd -p -c 256 | tr -d '\n') - fi - - hash=$(echo -n "$hash" | tr -d '[:space:]' | tr '[:upper:]' '[:lower:]') - echo "0x$hash" -} - -# Function to send a transaction that generates an external transaction -send_ext_txn() { - local sender=$1 - local receiver=$2 - local value=$3 - local token=$4 - - local tx_hash=$(generate_tx_hash) - - print_info "Generating external transaction..." - echo -e "${BLUE}Details:${NC}" - echo " Sender: $sender" - echo " Receiver: $receiver" - echo " Value: $value" - echo " Token: $token" - echo " TX Hash: $tx_hash" - echo "" - - # Build request with generate_ext_txn flag set to true - local params="{\"sender\":\"$sender\",\"receiver\":\"$receiver\",\"value\":$value,\"token\":\"$token\",\"hash\":\"$tx_hash\",\"generate_ext_txn\":true}" - local request="{\"jsonrpc\":\"2.0\",\"method\":\"sendTransaction\",\"params\":[$params],\"id\":$REQUEST_ID}" - - echo -e "${BLUE}Request:${NC}" - echo "$request" | jq '.' 2>/dev/null || echo "$request" - - # Send transaction - local response=$(curl -s -X POST "$API_URL" -H "$CONTENT_TYPE" -d "$request") - local curl_exit_code=$? - - echo -e "${GREEN}Response:${NC}" - echo "$response" | jq '.' 2>/dev/null || echo "$response" - - # Check if curl failed - if [ $curl_exit_code -ne 0 ]; then - print_error "Request failed - server not responding (curl exit code: $curl_exit_code)" - return 1 - fi - - # Check if response contains error - if echo "$response" | grep -q '"error"'; then - print_error "Transaction failed" - return 1 - fi - - print_success "Transaction sent successfully!" - - # Wait and check status - print_info "Waiting for transaction to be processed..." - sleep $STATUS_SLEEP - - # Check transaction status - check_tx_status "$tx_hash" - - return 0 -} - -# Function to check transaction status -check_tx_status() { - local tx_hash=$1 - print_info "Checking status for transaction: $tx_hash" - - local params="\"$tx_hash\"" - local status="" - local attempt=0 - local max_attempts=${STATUS_MAX_ATTEMPTS} - - while [ $attempt -lt $max_attempts ]; do - attempt=$((attempt + 1)) - - local request="{\"jsonrpc\":\"2.0\",\"method\":\"getTransactionStatus\",\"params\":[${params}],\"id\":${REQUEST_ID}}" - REQUEST_ID=$((REQUEST_ID + 1)) - - echo -e "${BLUE}Status Check (attempt $attempt/${max_attempts}):${NC}" - - local response=$(curl -s -X POST "$API_URL" -H "$CONTENT_TYPE" -d "$request") - local curl_exit_code=$? - - echo "$response" | jq '.' 2>/dev/null || echo "$response" - - if [ $curl_exit_code -ne 0 ]; then - print_error "Status check failed - server not responding" - return 1 - fi - - # Extract status - if command -v jq >/dev/null 2>&1; then - status=$(echo "$response" | jq -r '.result // empty' 2>/dev/null) - else - status=$(echo "$response" | grep -oE '"result"\s*:\s*"[^"]*"' | sed -E 's/.*"result"\s*:\s*"(.*)".*/\1/' | head -n1) - fi - - status=$(echo -n "$status" | tr -d '\r\n' | tr '[:upper:]' '[:lower:]') - - if [ "$status" == "processed" ]; then - print_success "Transaction processed successfully!" - print_success "An external transaction to Sepolia has been generated!" - return 0 - elif [ "$status" == "failed" ]; then - print_error "Transaction failed" - return 1 - fi - - if [ $attempt -lt $max_attempts ]; then - print_info "Status: $status β€” sleeping ${STATUS_SLEEP}s and retrying" - sleep ${STATUS_SLEEP} - fi - done - - print_error "Could not determine final status after $max_attempts attempts (last status: $status)" - return 1 -} - -# Main function -main() { - print_header "EXTERNAL TRANSACTION GENERATOR" - - # Parse command line arguments - SENDER="${1:-alice}" - RECEIVER="${2:-bob}" - VALUE="${3:-100}" - TOKEN="${4:-USDT}" - - echo "" - echo -e "${YELLOW}Configuration:${NC}" - echo -e "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo -e "${BLUE}API Endpoint:${NC} $API_URL" - echo -e "${BLUE}Sender:${NC} $SENDER" - echo -e "${BLUE}Receiver:${NC} $RECEIVER" - echo -e "${BLUE}Value:${NC} $VALUE" - echo -e "${BLUE}Token:${NC} $TOKEN" - echo -e "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "" - - # Check if jq is installed - if ! command -v jq &> /dev/null; then - print_error "jq is not installed. Install it for better JSON formatting:" - echo " brew install jq (on macOS)" - echo " apt-get install jq (on Ubuntu/Debian)" - echo "" - print_info "Continuing without JSON formatting..." - fi - - # Check if API is running - print_info "Checking if API is running at $API_URL..." - if ! curl -s -f -X POST "$API_URL" -H "$CONTENT_TYPE" -d '{"jsonrpc":"2.0","method":"getTransactionStatus","params":["0x0"],"id":0}' > /dev/null 2>&1; then - print_error "API is not responding at $API_URL" - print_error "Please start the appchain server first with: docker-compose up" - exit 1 - fi - print_success "API is running!" - - echo "" - - # Send transaction that generates external transaction - send_ext_txn "$SENDER" "$RECEIVER" "$VALUE" "$TOKEN" - - if [ $? -eq 0 ]; then - echo "" - print_header "SUCCESS" - print_success "External transaction generated successfully!" - print_info "The transaction should appear on Sepolia testnet soon" - echo "" - else - echo "" - print_header "FAILED" - print_error "Failed to generate external transaction" - exit 1 - fi -} - -# Show usage if --help is provided -if [ "$1" == "--help" ] || [ "$1" == "-h" ]; then - echo "Usage: $0 [SENDER] [RECEIVER] [VALUE] [TOKEN]" - echo "" - echo "Arguments:" - echo " SENDER - Sender account name (default: alice)" - echo " RECEIVER - Receiver account name (default: bob)" - echo " VALUE - Amount to transfer (default: 100)" - echo " TOKEN - Token symbol (default: USDT)" - echo "" - echo "Examples:" - echo " $0 # Use defaults (alice -> bob, 100 USDT)" - echo " $0 alice charlie 500 BTC # alice -> charlie, 500 BTC" - echo "" - echo "Environment Variables:" - echo " API_URL - Override API endpoint (default: http://localhost:8080/rpc)" - echo "" - exit 0 -fi - -# Run main function -main "$@" diff --git a/go.mod b/go.mod index 74985e5..e02125e 100644 --- a/go.mod +++ b/go.mod @@ -3,14 +3,14 @@ module github.com/0xAtelerix/example go 1.25.0 require ( - github.com/0xAtelerix/sdk v0.1.7 - github.com/blocto/solana-go-sdk v1.30.0 + github.com/0xAtelerix/sdk v0.1.8-0.20260112084622-112a883b375f github.com/ethereum/go-ethereum v1.16.3 - github.com/holiman/uint256 v1.3.2 github.com/ledgerwatch/erigon-lib v1.0.0 github.com/ledgerwatch/log/v3 v3.9.0 + github.com/prometheus/client_golang v1.23.2 github.com/rs/zerolog v1.34.0 github.com/stretchr/testify v1.11.1 + gopkg.in/yaml.v3 v3.0.1 ) require ( @@ -19,6 +19,7 @@ require ( github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.24.0 // indirect + github.com/blocto/solana-go-sdk v1.30.0 // indirect github.com/buger/jsonparser v1.1.1 // indirect github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect @@ -34,6 +35,7 @@ require ( github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/go-stack/stack v1.8.1 // indirect github.com/goccy/go-json v0.10.5 // indirect + github.com/holiman/uint256 v1.3.2 // indirect github.com/invopop/jsonschema v0.13.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.14 // indirect @@ -44,7 +46,6 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.66.1 // indirect github.com/prometheus/procfs v0.17.0 // indirect @@ -63,7 +64,6 @@ require ( google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 // indirect google.golang.org/grpc v1.75.1 // indirect google.golang.org/protobuf v1.36.9 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) replace google.golang.org/genproto v0.0.0-20200825200019-8632dd797987 => google.golang.org/genproto v0.0.0-20250707201910-8d1bb00bc6a7 diff --git a/go.sum b/go.sum index 27d9637..d737e34 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= -github.com/0xAtelerix/sdk v0.1.7 h1:8cEVHTs2vL4LYAuNKwSQcxqssztOfDETzYtl9gvGnak= -github.com/0xAtelerix/sdk v0.1.7/go.mod h1:tYa+zDjpx3DrDnTrqU2gASr8BmEoDzz6XrPuPkNcx64= +github.com/0xAtelerix/sdk v0.1.8-0.20260112084622-112a883b375f h1:ULNYhBzaBJK9T8hF6mU/LredQmdYW74g9092Dl2C0vI= +github.com/0xAtelerix/sdk v0.1.8-0.20260112084622-112a883b375f/go.mod h1:tYa+zDjpx3DrDnTrqU2gASr8BmEoDzz6XrPuPkNcx64= github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI= diff --git a/monitoring/alert_rules.yml b/monitoring/alert_rules.yml new file mode 100644 index 0000000..5d1a092 --- /dev/null +++ b/monitoring/alert_rules.yml @@ -0,0 +1,20 @@ +groups: + - name: bridge_alerts + rules: + - alert: BridgeServiceDown + expr: up{job="bridge"} == 0 + for: 1m + labels: + severity: critical + annotations: + summary: "Bridge service is down" + description: "Bridge appchain has been unreachable for more than 1 minute." + + - alert: BridgeTransactionStuck + expr: bridge_oldest_pending_timestamp > 0 and (time() - bridge_oldest_pending_timestamp) > 300 + for: 0m + labels: + severity: critical + annotations: + summary: "Bridge transaction stuck" + description: "A bridge transaction has been pending for more than 5 minutes." diff --git a/monitoring/alertmanager.yml b/monitoring/alertmanager.yml new file mode 100644 index 0000000..b1aa341 --- /dev/null +++ b/monitoring/alertmanager.yml @@ -0,0 +1,45 @@ +global: + resolve_timeout: 5m + +route: + group_by: ['alertname', 'severity'] + group_wait: 30s + group_interval: 5m + repeat_interval: 4h + receiver: 'telegram' + + routes: + # Critical alerts - immediate notification + - match: + severity: critical + receiver: 'telegram' + repeat_interval: 1h + + # Warning alerts + - match: + severity: warning + receiver: 'telegram' + repeat_interval: 4h + +receivers: + - name: 'telegram' + telegram_configs: + - bot_token: '${TELEGRAM_BOT_TOKEN}' + chat_id: ${TELEGRAM_CHAT_ID} + parse_mode: 'HTML' + message: | + {{ if eq .Status "firing" }}🚨{{ else }}βœ…{{ end }} {{ .Status | toUpper }} + + {{ range .Alerts }} + {{ .Labels.alertname }} + Severity: {{ .Labels.severity }} + {{ .Annotations.description }} + + {{ end }} + +inhibit_rules: + - source_match: + severity: 'critical' + target_match: + severity: 'warning' + equal: ['alertname'] diff --git a/monitoring/grafana/dashboards/bridge.json b/monitoring/grafana/dashboards/bridge.json new file mode 100644 index 0000000..3852e3e --- /dev/null +++ b/monitoring/grafana/dashboards/bridge.json @@ -0,0 +1,90 @@ +{ + "editable": true, + "panels": [ + { + "gridPos": { "h": 4, "w": 6, "x": 0, "y": 0 }, + "id": 1, + "options": { + "colorMode": "value", + "graphMode": "area", + "reduceOptions": { "calcs": ["lastNotNull"] } + }, + "targets": [{ "expr": "sum(increase(bridge_transactions_total[24h]))", "refId": "A" }], + "title": "Transactions (24h)", + "type": "stat" + }, + { + "gridPos": { "h": 4, "w": 6, "x": 6, "y": 0 }, + "id": 2, + "fieldConfig": { + "defaults": { + "thresholds": { + "steps": [ + { "color": "green", "value": null }, + { "color": "yellow", "value": 10 }, + { "color": "red", "value": 50 } + ] + } + } + }, + "options": { "colorMode": "value", "graphMode": "area", "reduceOptions": { "calcs": ["lastNotNull"] } }, + "targets": [{ "expr": "bridge_transactions_pending", "refId": "A" }], + "title": "Pending", + "type": "stat" + }, + { + "gridPos": { "h": 4, "w": 6, "x": 12, "y": 0 }, + "id": 3, + "options": { "colorMode": "value", "graphMode": "area", "reduceOptions": { "calcs": ["lastNotNull"] } }, + "targets": [{ "expr": "bridge_transactions_completed", "refId": "A" }], + "title": "Completed", + "type": "stat" + }, + { + "gridPos": { "h": 4, "w": 6, "x": 18, "y": 0 }, + "id": 4, + "fieldConfig": { + "defaults": { + "mappings": [ + { "options": { "0": { "text": "DOWN" } }, "type": "value" }, + { "options": { "1": { "text": "UP" } }, "type": "value" } + ], + "thresholds": { + "steps": [ + { "color": "red", "value": null }, + { "color": "green", "value": 1 } + ] + } + } + }, + "options": { "colorMode": "value", "graphMode": "none", "reduceOptions": { "calcs": ["lastNotNull"] } }, + "targets": [{ "expr": "up{job=\"bridge\"}", "refId": "A" }], + "title": "Service Status", + "type": "stat" + }, + { + "gridPos": { "h": 8, "w": 12, "x": 0, "y": 4 }, + "id": 5, + "fieldConfig": { "defaults": { "custom": { "drawStyle": "line", "fillOpacity": 10 } } }, + "options": { "legend": { "displayMode": "list", "placement": "bottom" } }, + "targets": [{ "expr": "rate(bridge_transactions_total[5m])", "legendFormat": "{{source_chain}} -> {{dest_chain}}", "refId": "A" }], + "title": "Transaction Rate", + "type": "timeseries" + }, + { + "gridPos": { "h": 8, "w": 12, "x": 12, "y": 4 }, + "id": 6, + "fieldConfig": { "defaults": { "custom": { "drawStyle": "line", "fillOpacity": 10 } } }, + "options": { "legend": { "displayMode": "list", "placement": "bottom" } }, + "targets": [{ "expr": "bridge_last_processed_block", "legendFormat": "Chain {{chain_id}}", "refId": "A" }], + "title": "Last Processed Block", + "type": "timeseries" + } + ], + "refresh": "10s", + "schemaVersion": 38, + "tags": ["bridge"], + "time": { "from": "now-1h", "to": "now" }, + "title": "Pelagos Bridge", + "uid": "pelagos-bridge" +} diff --git a/monitoring/grafana/provisioning/dashboards/default.yml b/monitoring/grafana/provisioning/dashboards/default.yml new file mode 100644 index 0000000..0bcf3d8 --- /dev/null +++ b/monitoring/grafana/provisioning/dashboards/default.yml @@ -0,0 +1,11 @@ +apiVersion: 1 + +providers: + - name: 'default' + orgId: 1 + folder: '' + type: file + disableDeletion: false + editable: true + options: + path: /var/lib/grafana/dashboards diff --git a/monitoring/grafana/provisioning/datasources/prometheus.yml b/monitoring/grafana/provisioning/datasources/prometheus.yml new file mode 100644 index 0000000..bb009bb --- /dev/null +++ b/monitoring/grafana/provisioning/datasources/prometheus.yml @@ -0,0 +1,9 @@ +apiVersion: 1 + +datasources: + - name: Prometheus + type: prometheus + access: proxy + url: http://prometheus:9090 + isDefault: true + editable: false diff --git a/monitoring/prometheus.yml b/monitoring/prometheus.yml new file mode 100644 index 0000000..1da4ccd --- /dev/null +++ b/monitoring/prometheus.yml @@ -0,0 +1,29 @@ +global: + scrape_interval: 15s + evaluation_interval: 15s + +alerting: + alertmanagers: + - static_configs: + - targets: + - alertmanager:9093 + +rule_files: + - /etc/prometheus/alert_rules.yml + +scrape_configs: + # Prometheus self-monitoring + - job_name: 'prometheus' + static_configs: + - targets: ['localhost:9090'] + + # Bridge appchain metrics + - job_name: 'bridge' + static_configs: + - targets: ['appchain:9091'] + metrics_path: /metrics + + # Alertmanager metrics + - job_name: 'alertmanager' + static_configs: + - targets: ['alertmanager:9093'] diff --git a/pelacli.example.yaml b/pelacli.example.yaml deleted file mode 100644 index a71e432..0000000 --- a/pelacli.example.yaml +++ /dev/null @@ -1,148 +0,0 @@ -# ============================================================================= -# PELACLI CONFIGURATION -# ============================================================================= -# Copy this file to pelacli.yaml and modify as needed. -# All fields are optional - defaults will be used if not specified. -# -# Run with: pelacli consensus --config=pelacli.yaml -# ============================================================================= - -# ============================================================================= -# DATA DIR -# ============================================================================= -# Root data directory shared with appchain -# Both appchain and pelacli must use the same data_dir for communication -# Default: /data (container path, works with docker-compose default mounts) -data_dir: "/data" - -# ============================================================================= -# CUSTOM PATHS (OPTIONAL) -# ============================================================================= -# Override specific directory paths. If not set, paths are derived from data_dir. -# Useful for advanced deployments requiring non-standard directory layouts. -# -# custom_paths: -# # Fetcher database directory -# # Default: {data_dir}/consensus/fetcher-db -# fetcher_db_dir: "/custom/path/to/fetcher-db" -# -# # Consensus events directory -# # Default: {data_dir}/consensus/events -# events_dir: "/custom/path/to/events" -# -# # Transaction batch directory -# # Default: {data_dir}/consensus/txbatch -# txbatch_dir: "/custom/path/to/txbatch" -# -# # Multichain root directory -# # Individual chain paths: {multichain_dir}/{chainID} -# # Default: {data_dir}/multichain -# multichain_dir: "/custom/path/to/multichain" - -# ============================================================================= -# CONSENSUS SETTINGS -# ============================================================================= -consensus: - # How often to poll appchain for new transactions - # Default: 100ms - ask_period: 100ms - - # Port for fetcher gRPC server (internal communication) - # Default: 8088 - fetcher_port: 8088 - -# ============================================================================= -# API SERVER SETTINGS -# ============================================================================= -api: - # Port for external transactions REST API (for explorer, etc.) - # Set to 0 to disable the API server - # Default: 8081 - port: 8081 - -# ============================================================================= -# AVAIL DA SETTINGS (Data Availability Layer) -# ============================================================================= -# Leave api_key empty to disable Avail DA integration -avail_da: - # Avail Turbo API key (empty = disabled) - # Get your API key from: https://turbo.availproject.org - api_key: "" - - # Avail Turbo API URL - # Default: https://staging.turbo-api.availproject.org - url: "https://staging.turbo-api.availproject.org" - -# ============================================================================= -# APPCHAIN CONNECTIONS -# ============================================================================= -# Configure which appchains pelacli should connect to for fetching transactions. -# If not specified, defaults to chain_id=42 at appchain:9090 (Docker service name) -# -# appchains: -# - chain_id: 42 -# address: "appchain:9090" # Docker service name for inter-container communication - -# ============================================================================= -# READ CHAINS - WSS connections for reading external chain blocks (oracle) -# ============================================================================= -# If not specified, defaults to Sepolia + Amoy with built-in WSS endpoints. -# DBPath is auto-computed from data_dir (e.g., ./data/multichain/11155111/). -# You can add any EVM or SVM compatible chain here. -# -# Block offset values: -# -4 = safe (2 blocks behind latest) -# -3 = finalized (most secure, ~15 min delay on Ethereum) -# -2 = latest (no delay, default for testnets) -# -1 = pending -# 0 = not set (uses default: -3 finalized for core, -2 latest for pelacli) -# 1+ = fixed offset from latest (e.g., 2 means if latest is block X, you get X-2) -# -# read_chains: -# - chain_id: 11155111 # Ethereum Sepolia -# api_key: "wss://my-custom-sepolia-endpoint.com" -# start_block: 0 # 0 = start from latest block or give specific block number -# block_offset: -2 # -3=finalized, -2=latest, -4=safe -# blocks_to_keep: 0 # 0 = no pruning (keep all blocks) -# prune_interval_seconds: 0 # 0 = default (5 minutes) -# -# - chain_id: 80002 # Polygon Amoy -# api_key: "wss://my-custom-amoy-endpoint.com" -# start_block: 0 -# block_offset: -2 -# blocks_to_keep: 1000 # Keep last 1000 blocks -# prune_interval_seconds: 300 # Prune every 5 minutes - -# ============================================================================= -# WRITE CHAINS - RPC connections for sending transactions to external chains -# ============================================================================= -# If not specified, defaults to Sepolia + Amoy with built-in RPC endpoints. -# Configure to send cross-chain transactions to external blockchains. -# -# write_chains: -# # Override Sepolia with your own RPC and key -# - chain_id: 11155111 -# rpc_url: "https://eth-sepolia.g.alchemy.com/v2/YOUR_API_KEY" -# private_key: "your-private-key-hex-without-0x-prefix" -# pelagos_contract: "0x922e02fFbDe8ABbF3058ccC3f9433018A2ff8C1d" -# -# # Override Polygon Amoy with your own RPC and key -# - chain_id: 80002 -# rpc_url: "https://polygon-amoy.g.alchemy.com/v2/YOUR_API_KEY" -# private_key: "your-private-key-hex-without-0x-prefix" -# pelagos_contract: "0x98D34a83c45FEae289f6FA72ba009Ad49F3D26ED" -# -# # Add a custom chain not in defaults -# - chain_id: 421614 # Arbitrum Sepolia -# rpc_url: "https://arb-sepolia.g.alchemy.com/v2/YOUR_API_KEY" -# private_key: "your-private-key-hex" -# pelagos_contract: "0xYourPelagosContractAddress" - -# ============================================================================= -# DEFAULT PELAGOS CONTRACT ADDRESSES (for reference, They might change) -# ============================================================================= -# Ethereum Sepolia (11155111): 0x922e02fFbDe8ABbF3058ccC3f9433018A2ff8C1d -# Polygon Amoy (80002): 0x98D34a83c45FEae289f6FA72ba009Ad49F3D26ED -# -# Deploy your own Pelagos and AppChain contracts using: -# https://github.com/0xAtelerix/sdk/tree/main/contracts diff --git a/pelacli.yaml b/pelacli.yaml new file mode 100644 index 0000000..f64905f --- /dev/null +++ b/pelacli.yaml @@ -0,0 +1,27 @@ +data_dir: "/data" + +consensus: + ask_period: 1s + +api: + port: 8081 + +appchains: + - chain_id: 42 + address: "appchain:9090" + +read_chains: # Sepolia default available in pelacli for read + - chain_id: 50591822 # Stavanger testnet + api_key: "wss://stavanger-rpc.eu-north-2.gateway.fm/ws" + block_offset: 1 + +write_chains: + - chain_id: 11155111 # Ethereum Sepolia + rpc_url: "https://eth-sepolia.g.alchemy.com/v2/bEzb7Rb14hzR79tLw-gfy" + private_key: "e98608987ecc7b3315bee23e86a5c1cfb6288159d0a3c2009335f4494bd4579a" + pelagos_contract: "0x049FBea1295B569378Fe0D5AB965131743f332b9" + + - chain_id: 50591822 # Stavanger testnet + rpc_url: "https://stavanger-rpc.eu-north-2.gateway.fm" + private_key: "e98608987ecc7b3315bee23e86a5c1cfb6288159d0a3c2009335f4494bd4579a" + pelagos_contract: "0x416b560B03e6d9EF473bf57ccbb2A569AF5d0736" diff --git a/readme.md b/readme.md deleted file mode 100644 index 6168029..0000000 --- a/readme.md +++ /dev/null @@ -1,352 +0,0 @@ -# Pelagos Appchain β€” Build an Appchain with the Go SDK - -> This repo is a **skeleton** for building your own appchain on top of the Pelagos Go SDK. -> It comes with a runnable **docker-compose** stack that includes both your appchain node and a **consensus** (`pelacli`) that simulates consensus in your local environment and feeds external chain data. - -## Table of Contents - -- [What you get out of the box](#what-you-get-out-of-the-box) -- [Key concepts & execution model](#key-concepts--execution-model) -- [Project layout](#project-layout) -- [Docker β€” compose stack](#docker--compose-stack) -- [Zero-Config Setup](#zero-config-setup) -- [Custom Configuration (Optional)](#custom-configuration-optional) -- [Build & Run](#build--run) -- [JSON-RPC quickstart](#json-rpc-quickstart) -- [Code walkthrough (where to extend)](#code-walkthrough-where-to-extend) -- [Flags (quick reference)](#flags-quick-reference) -- [Additional Resources](#additional-resources) - -## What you get out of the box - -* A minimal **block type** (`Block`) that satisfies `apptypes.AppchainBlock` from the SDK. -* A **transaction** (`Transaction`) and **receipt** (`Receipt`) implementing a simple token transfer with balances in MDBX. -* A **stateless external-block adapter** (`StateTransition`) that shows how to fetch/inspect Ethereum/Solana data via `MultichainStateAccess`. -* **Cross-chain transaction support** via `pelacli` external transaction configuration for sending transactions to external networks. -* **Genesis seeding** to fund demo users (`alice`, `bob`, …) with USDT/BTC/ETH balances. -* **Buckets** (tables) for app state (`appaccounts`), receipts, blocks, checkpoints, etc. -* A runnable `main.go` that wires the SDK, DBs, tx-pool, validator set, the appchain loop, and default **JSON-RPC**. -* One **custom JSON-RPC** (`getBalance`) + **standard** ones (`sendTransaction`, `getTransactionStatus`, `getTransactionReceipt`, …). -* A **docker-compose** that runs the node together with `pelacli` so your txs actually progress. - -## Key concepts & execution model - -1. **Clients** submit transactions via JSON-RPC (`sendTransaction`) into the **tx pool**. -2. The **consensus** (`pelacli`) periodically: - - * pulls txs via the appchain's **gRPC emitter API** (`CreateInternalTransactionsBatch`), - * writes **tx-batches** to an MDBX database (`txbatch`), - * appends **events** (referencing those batches + external blocks) to the event stream file. -3. The appchain run loop consumes **events + tx-batches**, executes your `Transaction.Process` inside a DB write transaction, persists **receipts**, builds a **block**, and writes a **checkpoint**. -4. JSON-RPC exposes tx **status**, **receipts**, and your **custom methods**. - -> Status lifecycle: **Pending** (in tx pool) β†’ **Batched** (pulled by pelacli) β†’ **Processed/Failed** (after your txn processing logic runs in a block). - -* **Transactions don't auto-finalize.** - Without the **consensus** you'll only ever see `Pending`. This compose includes `pelacli` to move them forward. - -* **The appchain waits for real data sources.** - It blocks until both exist: - - * the **event file**: `/epoch_1.data`, - * the **tx-batch MDBX**: `` with the `txbatch` table. - `pelacli` creates and updates both. - -* **Multichain access uses local SQLite** - The SDK reads external chain state from **SQLite databases** on disk. `pelacli` populates and updates them automatically. By default, Sepolia and Polygon Amoy are enabled. Add additional networks via `read_chains` in `pelacli.yaml` (see [`pelacli.example.yaml`](./pelacli.example.yaml)). - -* **Cross-chain transaction flow** - - **Read**: `pelacli` fetches data from external chains β†’ stores in SQLite β†’ appchain reads via `MultichainStateAccess` - - **Write**: appchain generates `ExternalTransaction` β†’ `pelacli` sends to Pelagos contract β†’ Pelagos routes to specific AppChain contract based on appchainID - - **Custom contracts**: Deploy your own AppChain contracts on external chains using the contracts in the [SDK contracts folder](https://github.com/0xAtelerix/sdk/tree/main/contracts) for more advanced cross-chain interactions - - -## Project layout - -``` -. -β”œβ”€ application/ -β”‚ β”œβ”€ block.go # Block type + constructor -β”‚ β”œβ”€ buckets.go # App buckets (tables) -β”‚ β”œβ”€ errors.go # App-level errors -β”‚ β”œβ”€ genesis.go # One-time state seeding (demo balances) -β”‚ β”œβ”€ receipt.go # Receipt type -β”‚ β”œβ”€ state_transition.go # External-chain ingestion (stateless) -β”‚ β”œβ”€ transaction.go # Business logic (transfers) -β”‚ └─ api/ -β”‚ β”œβ”€ api.go # Custom JSON-RPC methods (getBalance) -β”‚ └─ middleware.go # CORS and other middleware -β”œβ”€ cmd/ -β”‚ β”œβ”€ config.go # Config struct and YAML loading -β”‚ β”œβ”€ main.go # Wiring & run loop (the app binary) -β”‚ └─ main_test.go # End-to-end integration test -β”œβ”€ data/ # Shared volume (created at runtime) -β”œβ”€ config.example.yaml # Example appchain config -β”œβ”€ pelacli.example.yaml # Example pelacli config -β”œβ”€ Dockerfile # Dockerfile for the appchain node -β”œβ”€ docker-compose.yml # Compose for appchain + pelacli -└─ test_txns.sh # Test script for sending transactions -``` - - -## Docker β€” compose stack - -This compose runs **both** your appchain and the **pelacli** streamer with **zero configuration required**. - -### `docker-compose.yml` - -```yaml -services: - pelacli: - container_name: pelacli - image: pelagosnetwork/pelacli:latest - working_dir: /app - volumes: - - ./data:/app/data - command: - - consensus - # Zero-config: uses SDK defaults - # - appchain: 42 @ :9090 - # - chains: Sepolia, Polygon Amoy - # - paths: ./data/events, ./data/multichain - - appchain: - build: - context: . - dockerfile: Dockerfile - working_dir: /app - pid: "service:pelacli" - image: appchain:latest - volumes: - - ./data:/app/data - ports: - - "9090:9090" - - "8080:8080" - depends_on: - - pelacli - # Zero-config: uses SDK defaults - # - chain_id: 42 - # - emitter: :9090 - # - rpc: :8080 - # - data: ./data -``` - -**Data directory structure** - -Both containers share the same `./data` volume: - -``` -./data/ -β”œβ”€β”€ multichain/ # External chain data (written by pelacli, read by appchain) -β”‚ β”œβ”€β”€ 11155111/ # Ethereum Sepolia -β”‚ β”‚ └── sqlite -β”‚ └── 80002/ # Polygon Amoy -β”‚ └── sqlite -β”œβ”€β”€ events/ # Consensus events (written by pelacli, read by appchain) -β”‚ └── epoch_1.data -β”œβ”€β”€ fetcher/ # Transaction batches (written by pelacli, read by appchain) -β”‚ └── 42/ # Per-appchain (chainID=42) -β”‚ └── mdbx.dat -β”œβ”€β”€ appchain/ # Appchain state (written by appchain) -β”‚ └── 42/ -β”‚ └── mdbx.dat -└── local/ # Local node data like txpool (written by appchain) - └── 42/ - └── mdbx.dat -``` - -* **pelacli** writes: `events/`, `fetcher/`, `multichain/` -* **appchain** writes: `appchain/`, `local/` -* **appchain** reads: `events/`, `fetcher/`, `multichain/` - -> Keep **ChainID=42** consistent across your code and pelacli config (both default to 42). - - -## Zero-Config Setup - -The default setup requires **no configuration files**. Pelacli comes with built-in testnet defaults: - -| Network | Chain ID | Pelagos Contract | -|---------|----------|------------------| -| Sepolia | 11155111 | `0x922e02fFbDe8ABbF3058ccC3f9433018A2ff8C1d` | -| Polygon Amoy | 80002 | `0x98D34a83c45FEae289f6FA72ba009Ad49F3D26ED` | - -Just clone and run: - -```bash -git clone https://github.com/0xAtelerix/example -cd example -docker compose up -d -``` - - -## Custom Configuration (Optional) - -For production or custom setups, copy the example config files and modify as needed. - -### Appchain Configuration - -```bash -cp config.example.yaml config.yaml -# Edit config.yaml with your settings -./appchain -config=config.yaml -``` - -Available settings in `config.yaml`: - -| Field | Default | Description | -|-------|---------|-------------| -| `chain_id` | 42 | Appchain identifier (must match pelacli) | -| `data_dir` | `./data` | Root data directory shared with pelacli | -| `emitter_port` | `:9090` | gRPC port for emitter API | -| `rpc_port` | `:8080` | HTTP port for JSON-RPC server | -| `log_level` | 1 | Log verbosity: 0=debug, 1=info, 2=warn, 3=error | - -See [`config.example.yaml`](./config.example.yaml) for the complete example. - -### Pelacli Configuration - -```bash -cp pelacli.example.yaml pelacli.yaml -# Edit pelacli.yaml with your settings -pelacli consensus --config=pelacli.yaml -``` - -Key configuration sections: - -| Section | Default | Description | -|---------|---------|-------------| -| `data_dir` | `./data` | Root data directory (must match appchain's `data_dir`) | -| `consensus` | - | Polling interval and fetcher settings | -| `api` | - | External transactions REST API settings | -| `avail_da` | - | Avail DA integration (optional) | -| `appchains` | `42@:9090` | Which appchains to connect to | -| `read_chains` | Sepolia, Amoy | WSS endpoints for reading external blocks | -| `write_chains` | Sepolia, Amoy | RPC endpoints for sending cross-chain transactions | - -**Block offset values** (for `read_chains`): -- `-4` = safe (2 blocks behind latest) -- `-3` = finalized (most secure, ~15 min delay on Ethereum) -- `-2` = latest (no delay, default for testnets) -- `1`, `2`, etc. = fixed offset from latest (e.g., `2` means if latest is block X, you get block X-2) - -See [`pelacli.example.yaml`](./pelacli.example.yaml) for all available options with detailed comments. - - -## Build & Run - -1. **Start (zero-config):** - - ```bash - docker compose up -d - ``` - -2. **Check health:** - - ```bash - curl -s http://localhost:8080/health | jq . - ``` - -3. **Tail logs:** - - ```bash - docker compose logs -f pelacli - docker compose logs -f appchain - ``` - -4. **Test:** - - ```bash - ./test_txns.sh - ``` - -> On the first run, pelacli will populate SQLite databases and start producing events/tx-batches. Your appchain waits until the event file and tx-batch DB exist, then begins processing. - ---- - -## JSON-RPC quickstart - -### Send a transfer - -```bash -TX_HASH=0x$(date +%s%N | sha256sum | awk '{print $1}') - -curl -s http://localhost:8080/rpc \ - -H 'Content-Type: application/json' \ - -d '{ - "jsonrpc":"2.0", - "method":"sendTransaction", - "params":[{"sender":"alice","receiver":"bob","value":1000,"token":"USDT","hash":"'"$TX_HASH"'"}], - "id":1 - }' | jq -``` - -### Check status - -```bash -curl -s http://localhost:8080/rpc \ - -H 'Content-Type: application/json' \ - -d '{"jsonrpc":"2.0","method":"getTransactionStatus","params":["'"$TX_HASH"'"],"id":2}' | jq -``` - -### Get receipt (after Processed/Failed) - -```bash -curl -s http://localhost:8080/rpc \ - -H 'Content-Type: application/json' \ - -d '{"jsonrpc":"2.0","method":"getTransactionReceipt","params":["'"$TX_HASH"'"],"id":3}' | jq -``` - -### Custom method: balance - -```bash -curl -s http://localhost:8080/rpc \ - -H 'Content-Type: application/json' \ - -d '{"jsonrpc":"2.0","method":"getBalance","params":[{"user":"alice","token":"USDT"}],"id":4}' | jq -``` - -> Demo balances are seeded on first start by `InitializeGenesis`. - - -## Code walkthrough (where to extend) - -* **`application/transaction.go` β†’ `Process`** - Your business logic lives here (validation, state writes, receipts). - Return `[]ExternalTransaction` if you want to emit cross-chain transactions from your appchain to external blockchains. - -* **`application/state_transition.go` β†’ `ProcessBlock`** - Turn **external blocks/receipts** (fetched via `MultichainStateAccess`) into internal transactions that your appchain will execute (e.g., processing deposits from external chains). Keep this layer **stateless**; all state changes happen in `Transaction.Process`. - -* **`application/block.go` β†’ `BlockConstructor`** - Builds per-block artifacts. Currently uses a **stub** state root; replace `StubRootCalculator` with your own when ready. - -* **`application/buckets.go`** - Add your own tables and merge them with `gosdk.DefaultTables()` in `main.go`. - -* **`application/api/api.go`** - Add read-only custom JSON-RPC methods for your UI. - -* **`application/api/middleware.go`** (Optional) - Configure Auth, Logging, and HTTP middleware for your JSON-RPC server. - - -## Flags (quick reference) - -The appchain uses SDK defaults. Override with config file: - -* `-config=config.yaml` β€” Path to YAML config file (optional) - -All settings can be configured in the YAML file: -* `chain_id` β€” Appchain identifier (default: 42) -* `data_dir` β€” Root data directory (default: `./data`) -* `emitter_port` β€” gRPC emitter port (default: `:9090`) -* `rpc_port` β€” JSON-RPC server port (default: `:8080`) -* `log_level` β€” Log verbosity 0-3 (default: 1=info) - -## Additional Resources - -- [Pelagos SDK](https://github.com/0xAtelerix/sdk) β€” Core SDK documentation and source code -- [SDK Contracts](https://github.com/0xAtelerix/sdk/tree/main/contracts) β€” Deploy your own AppChain contracts on external chains for custom cross-chain functionality - ---- -**Happy building!** For questions or issues, check the [issues](https://github.com/0xAtelerix/example/issues) or reach out to the Pelagos community. diff --git a/test_txns.sh b/test_txns.sh deleted file mode 100755 index 5e078bc..0000000 --- a/test_txns.sh +++ /dev/null @@ -1,677 +0,0 @@ -#!/bin/bash - -# Basic Transaction API Test Script -# This script tests basic transfer operations between accounts - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -# API endpoints -API_URL="http://localhost:8080/rpc" -PELACLI_API_URL="http://localhost:8081/rpc" -SEPOLIA_RPC_URL="https://ethereum-sepolia-rpc.publicnode.com" -CONTENT_TYPE="Content-Type: application/json" - -# Counter for request IDs -REQUEST_ID=1 - -# Retry settings for transaction status polling -STATUS_MAX_ATTEMPTS=5 -STATUS_SLEEP=1 - -# Arrays to store test results for summary -declare -a TEST_RESULTS -declare -a TEST_METHODS -declare -a TEST_DESCRIPTIONS -TOTAL_TESTS=0 -SUCCESSFUL_TESTS=0 -FAILED_TESTS=0 - -# Function to print colored messages -print_header() { - echo -e "\n${BLUE}════════════════════════════════════════════════════════════════${NC}" - echo -e "${YELLOW}$1${NC}" - echo -e "${BLUE}════════════════════════════════════════════════════════════════${NC}" -} - -print_success() { - echo -e "${GREEN}βœ“ $1${NC}" -} - -print_error() { - echo -e "${RED}βœ— $1${NC}" -} - -print_info() { - echo -e "${BLUE}β„Ή $1${NC}" -} - -# Function to make API call and pretty print response -api_call() { - local method=$1 - local params=$2 - local description=$3 - - print_info "Testing: $description" - - # Ensure params is a JSON array (server expects params as an array) - local trimmed=$(echo -n "$params" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') - if [[ "$trimmed" == "["* ]]; then - # already an array - : - elif [[ "$trimmed" == "{}" || -z "$trimmed" ]]; then - params="[]" - else - params="[$params]" - fi - - local request="{\"jsonrpc\":\"2.0\",\"method\":\"$method\",\"params\":$params,\"id\":$REQUEST_ID}" - - # Display request - echo -e "${BLUE}Request:${NC}" - echo "$request" | jq '.' 2>/dev/null || echo "$request" - - # Store test results for summary - TEST_METHODS[$TOTAL_TESTS]="$method" - TEST_DESCRIPTIONS[$TOTAL_TESTS]="$description" - - # Make API call - local response=$(curl -s -X POST "$API_URL" -H "$CONTENT_TYPE" -d "$request") - local curl_exit_code=$? - - # Display response - echo -e "${GREEN}Response:${NC}" - echo "$response" | jq '.' 2>/dev/null || echo "$response" - - # Check if curl failed (server not running) - if [ $curl_exit_code -ne 0 ]; then - print_error "Request failed - server not responding (curl exit code: $curl_exit_code)" - TEST_RESULTS[$TOTAL_TESTS]="FAILED" - FAILED_TESTS=$((FAILED_TESTS + 1)) - # Check if response contains error - elif echo "$response" | grep -q '"error"'; then - print_error "Request failed" - TEST_RESULTS[$TOTAL_TESTS]="FAILED" - FAILED_TESTS=$((FAILED_TESTS + 1)) - else - print_success "Request successful" - TEST_RESULTS[$TOTAL_TESTS]="SUCCESS" - SUCCESSFUL_TESTS=$((SUCCESSFUL_TESTS + 1)) - fi - - # Store the transaction hash if present - if echo "$response" | grep -q '"hash"'; then - TX_HASH=$(echo "$response" | jq -r '.result.hash // .result.tx_hash // ""' 2>/dev/null) - if [ ! -z "$TX_HASH" ]; then - echo -e "${BLUE}Transaction Hash: $TX_HASH${NC}" - fi - fi - - TOTAL_TESTS=$((TOTAL_TESTS + 1)) - REQUEST_ID=$((REQUEST_ID + 1)) - echo "" -} - -# Function to check transaction status -check_tx_status() { - local tx_hash=$1 - print_info "Checking status for transaction: $tx_hash" - - # Pass the tx hash as a JSON string so params becomes [""] - local params="\"$tx_hash\"" - - # Record this check in the test summary arrays so the final report shows it - TEST_METHODS[$TOTAL_TESTS]="getTransactionStatus" - TEST_DESCRIPTIONS[$TOTAL_TESTS]="Transaction Status Check for $tx_hash" - - local status="" - local attempt=0 - local max_attempts=${STATUS_MAX_ATTEMPTS} - - while [ $attempt -lt $max_attempts ]; do - attempt=$((attempt + 1)) - - # Build and send getTransactionStatus request - local request="{\"jsonrpc\":\"2.0\",\"method\":\"getTransactionStatus\",\"params\":[${params}],\"id\":${REQUEST_ID}}" - REQUEST_ID=$((REQUEST_ID + 1)) - - echo -e "${BLUE}Request (attempt $attempt/${max_attempts}):${NC}" - echo "$request" | jq '.' 2>/dev/null || echo "$request" - - local response=$(curl -s -X POST "$API_URL" -H "$CONTENT_TYPE" -d "$request") - local curl_exit_code=$? - - echo -e "${GREEN}Response:${NC}" - echo "$response" | jq '.' 2>/dev/null || echo "$response" - - # Check if curl failed - if [ $curl_exit_code -ne 0 ]; then - print_error "Request failed - server not responding (curl exit code: $curl_exit_code)" - status="error" - break - fi - - # Extract status from response - if command -v jq >/dev/null 2>&1; then - status=$(echo "$response" | jq -r '.result // empty' 2>/dev/null) - else - status=$(echo "$response" | grep -oE '"result"\s*:\s*"[^"]*"' | sed -E 's/.*"result"\s*:\s*"(.*)".*/\1/' | head -n1) - fi - - # Normalize status - status=$(echo -n "$status" | tr -d '\r\n' | tr '[:upper:]' '[:lower:]') - - # Check if final status - if [ "$status" == "processed" ] || [ "$status" == "failed" ]; then - break - fi - - # If not final and not last attempt, sleep and retry - if [ $attempt -lt $max_attempts ]; then - print_info "Status: $status β€” sleeping ${STATUS_SLEEP}s and retrying" - sleep ${STATUS_SLEEP} - fi - done - - # Handle final status - if [ "$status" == "processed" ]; then - print_success "Transaction status: Processed" - TEST_RESULTS[$TOTAL_TESTS]="SUCCESS" - SUCCESSFUL_TESTS=$((SUCCESSFUL_TESTS + 1)) - elif [ "$status" == "failed" ]; then - print_error "Transaction status: Failed" - - # Fetch receipt for error details - local receipt_req="{\"jsonrpc\":\"2.0\",\"method\":\"getTransactionReceipt\",\"params\":[${params}],\"id\":${REQUEST_ID}}" - REQUEST_ID=$((REQUEST_ID + 1)) - - echo -e "${BLUE}Receipt Request:${NC}" - echo "$receipt_req" | jq '.' 2>/dev/null || echo "$receipt_req" - local receipt_resp=$(curl -s -X POST "$API_URL" -H "$CONTENT_TYPE" -d "$receipt_req") - local receipt_curl_exit=$? - - echo -e "${GREEN}Receipt Response:${NC}" - echo "$receipt_resp" | jq '.' 2>/dev/null || echo "$receipt_resp" - - # Check if receipt request failed - if [ $receipt_curl_exit -ne 0 ]; then - print_error "Receipt request failed - server not responding" - TEST_RESULTS[$TOTAL_TESTS]="FAILED" - FAILED_TESTS=$((FAILED_TESTS + 1)) - else - # Extract error from receipt - local err="" - if command -v jq >/dev/null 2>&1; then - err=$(echo "$receipt_resp" | jq -r '.result.error // empty' 2>/dev/null) - else - err=$(echo "$receipt_resp" | grep -oE '"error"\s*:\s*"[^"]*"' | sed -E 's/.*"error"\s*:\s*"(.*)".*/\1/' | head -n1) - fi - - if [ ! -z "$err" ]; then - print_error "Receipt error: $err" - - # Check if this is a business logic validation error (expected behavior) - if echo "$err" | grep -q "sender's balance not enough"; then - print_info "Note: This is expected validation behavior - insufficient balance is correctly rejected" - TEST_RESULTS[$TOTAL_TESTS]="SUCCESS (Validation)" - SUCCESSFUL_TESTS=$((SUCCESSFUL_TESTS + 1)) - else - TEST_RESULTS[$TOTAL_TESTS]="FAILED" - FAILED_TESTS=$((FAILED_TESTS + 1)) - fi - else - # No error details available - TEST_RESULTS[$TOTAL_TESTS]="FAILED" - FAILED_TESTS=$((FAILED_TESTS + 1)) - fi - fi - else - print_error "Could not determine final status after $max_attempts attempts (last status: $status)" - TEST_RESULTS[$TOTAL_TESTS]="PENDING" - fi - - TOTAL_TESTS=$((TOTAL_TESTS + 1)) - echo "" -} - -# Function to generate unique transaction hash (32-byte SHA-256) -generate_tx_hash() { - # Add random component to ensure uniqueness even when called rapidly - payload="$(date +%s%N)$RANDOM" - - if command -v sha256sum >/dev/null 2>&1; then - hash=$(printf "%s" "$payload" | sha256sum | awk '{print $1}') - elif command -v shasum >/dev/null 2>&1; then - # macOS shasum supports -a 256 - hash=$(printf "%s" "$payload" | shasum -a 256 | awk '{print $1}') - elif command -v openssl >/dev/null 2>&1; then - hash=$(printf "%s" "$payload" | openssl dgst -sha256 | awk '{print $NF}') - else - # Fallback: use md5sum if available, otherwise hex of the payload - if command -v md5sum >/dev/null 2>&1; then - hash=$(printf "%s" "$payload" | md5sum | awk '{print $1}') - else - hash=$(printf "%s" "$payload" | xxd -p -c 256 | tr -d '\n') - fi - fi - - # Normalize: remove whitespace, lowercase - hash=$(echo -n "$hash" | tr -d '[:space:]' | tr '[:upper:]' '[:lower:]') - echo "0x$hash" -} - -# Function to check balance for a user and token -check_balance() { - local user=$1 - local token=$2 - local description=$3 - - print_info "Checking balance: $description" >&2 - - # Build getBalance request - local params="{\"user\":\"$user\",\"token\":\"$token\"}" - - # Record this check in the test summary arrays - TEST_METHODS[$TOTAL_TESTS]="getBalance" - TEST_DESCRIPTIONS[$TOTAL_TESTS]="Balance check for $user ($token)" - - local request="{\"jsonrpc\":\"2.0\",\"method\":\"getBalance\",\"params\":[$params],\"id\":$REQUEST_ID}" - REQUEST_ID=$((REQUEST_ID + 1)) - - echo -e "${BLUE}Request:${NC}" >&2 - echo "$request" | jq '.' >&2 2>/dev/null || echo "$request" >&2 - - # Make API call - local response=$(curl -s -X POST "$API_URL" -H "$CONTENT_TYPE" -d "$request") - local curl_exit_code=$? - - echo -e "${GREEN}Response:${NC}" >&2 - echo "$response" | jq '.' >&2 2>/dev/null || echo "$response" >&2 - - # Check if curl failed - if [ $curl_exit_code -ne 0 ]; then - print_error "Balance check failed - server not responding (curl exit code: $curl_exit_code)" >&2 - TEST_RESULTS[$TOTAL_TESTS]="FAILED" - FAILED_TESTS=$((FAILED_TESTS + 1)) - TOTAL_TESTS=$((TOTAL_TESTS + 1)) - return 1 - fi - - # Check if response contains error - if echo "$response" | grep -q '"error"'; then - print_error "Balance check failed" >&2 - TEST_RESULTS[$TOTAL_TESTS]="FAILED" - FAILED_TESTS=$((FAILED_TESTS + 1)) - TOTAL_TESTS=$((TOTAL_TESTS + 1)) - return 1 - fi - - # Extract balance from response - local balance="" - if command -v jq >/dev/null 2>&1; then - balance=$(echo "$response" | jq -r '.result.balance // empty' 2>/dev/null) - else - balance=$(echo "$response" | grep -oE '"balance"\s*:\s*"[^"]*"' | sed -E 's/.*"balance"\s*:\s*"(.*)".*/\1/' | head -n1) - fi - - if [ -z "$balance" ]; then - print_error "Could not extract balance from response" >&2 - TEST_RESULTS[$TOTAL_TESTS]="FAILED" - FAILED_TESTS=$((FAILED_TESTS + 1)) - TOTAL_TESTS=$((TOTAL_TESTS + 1)) - return 1 - fi - - print_success "Balance check successful: $user has $balance $token" >&2 - TEST_RESULTS[$TOTAL_TESTS]="SUCCESS" - SUCCESSFUL_TESTS=$((SUCCESSFUL_TESTS + 1)) - TOTAL_TESTS=$((TOTAL_TESTS + 1)) - - # Return the balance for use in calling function (stdout only) - echo "$balance" -} - -# Function to get external transactions from pelacli and return the latest tx hash -# Calls ext_listTransactions on pelacli RPC -get_latest_external_txn() { - local min_count=$1 - - local attempt=0 - local max_attempts=15 - - while [ $attempt -lt $max_attempts ]; do - attempt=$((attempt + 1)) - - # Build ext_listTransactions request to pelacli (filter by fromChainId=42 which is our appchain) - local request="{\"jsonrpc\":\"2.0\",\"method\":\"ext_listTransactions\",\"params\":[{\"fromChainId\":42,\"limit\":1}],\"id\":${REQUEST_ID}}" - REQUEST_ID=$((REQUEST_ID + 1)) - - local response=$(curl -s -X POST "$PELACLI_API_URL" -H "$CONTENT_TYPE" -d "$request") - local curl_exit_code=$? - - if [ $curl_exit_code -ne 0 ]; then - if [ $attempt -lt $max_attempts ]; then - sleep 2 - continue - fi - return 1 - fi - - # Check for RPC error - local rpc_error=$(echo "$response" | jq -r '.error.message // empty' 2>/dev/null) - if [ -n "$rpc_error" ]; then - if [ $attempt -lt $max_attempts ]; then - sleep 2 - continue - fi - return 1 - fi - - # Get total count and latest tx hash - local total=$(echo "$response" | jq -r '.result.total // 0' 2>/dev/null) - if [ "$total" -ge "$min_count" ] 2>/dev/null; then - # Return the latest tx hash (first in the list) - echo "$response" | jq -r '.result.transactions[0].txHash // empty' 2>/dev/null - return 0 - fi - - if [ $attempt -lt $max_attempts ]; then - sleep 2 - fi - done - - return 1 -} - -# Function to verify external transaction on Sepolia using public RPC -# Queries eth_getTransactionReceipt to confirm the tx was mined -verify_tx_on_sepolia() { - local tx_hash=$1 - local description=$2 - - print_info "Verifying transaction on Sepolia: $tx_hash" - - # Record this check in the test summary arrays - TEST_METHODS[$TOTAL_TESTS]="eth_getTransactionReceipt" - TEST_DESCRIPTIONS[$TOTAL_TESTS]="$description" - - local attempt=0 - local max_attempts=30 # Up to 60 seconds for tx confirmation - - while [ $attempt -lt $max_attempts ]; do - attempt=$((attempt + 1)) - - # Query Sepolia RPC for transaction receipt - local request="{\"jsonrpc\":\"2.0\",\"method\":\"eth_getTransactionReceipt\",\"params\":[\"$tx_hash\"],\"id\":${REQUEST_ID}}" - REQUEST_ID=$((REQUEST_ID + 1)) - - echo -e "${BLUE}Request to Sepolia RPC (attempt $attempt/${max_attempts}):${NC}" - echo "$request" | jq '.' 2>/dev/null || echo "$request" - - local response=$(curl -s -X POST "$SEPOLIA_RPC_URL" -H "$CONTENT_TYPE" -d "$request") - local curl_exit_code=$? - - echo -e "${GREEN}Response:${NC}" - echo "$response" | jq '.' 2>/dev/null || echo "$response" - - # Check if curl failed - if [ $curl_exit_code -ne 0 ]; then - print_error "Request failed - Sepolia RPC not responding" - if [ $attempt -lt $max_attempts ]; then - print_info "Retrying in 2s..." - sleep 2 - continue - fi - TEST_RESULTS[$TOTAL_TESTS]="FAILED" - FAILED_TESTS=$((FAILED_TESTS + 1)) - TOTAL_TESTS=$((TOTAL_TESTS + 1)) - echo "" - return 1 - fi - - # Check for RPC error - local rpc_error=$(echo "$response" | jq -r '.error.message // empty' 2>/dev/null) - if [ -n "$rpc_error" ]; then - print_error "Sepolia RPC error: $rpc_error" - TEST_RESULTS[$TOTAL_TESTS]="FAILED" - FAILED_TESTS=$((FAILED_TESTS + 1)) - TOTAL_TESTS=$((TOTAL_TESTS + 1)) - echo "" - return 1 - fi - - # Check if receipt exists (transaction was mined) - local receipt_status=$(echo "$response" | jq -r '.result.status // empty' 2>/dev/null) - local block_number=$(echo "$response" | jq -r '.result.blockNumber // empty' 2>/dev/null) - - if [ -n "$receipt_status" ] && [ "$receipt_status" != "null" ]; then - if [ "$receipt_status" == "0x1" ]; then - print_success "Transaction confirmed on Sepolia!" - echo -e "${BLUE}Block Number: $block_number${NC}" - echo -e "${BLUE}Status: Success (0x1)${NC}" - TEST_RESULTS[$TOTAL_TESTS]="SUCCESS" - SUCCESSFUL_TESTS=$((SUCCESSFUL_TESTS + 1)) - TOTAL_TESTS=$((TOTAL_TESTS + 1)) - echo "" - return 0 - else - print_error "Transaction failed on Sepolia (status: $receipt_status)" - TEST_RESULTS[$TOTAL_TESTS]="FAILED" - FAILED_TESTS=$((FAILED_TESTS + 1)) - TOTAL_TESTS=$((TOTAL_TESTS + 1)) - echo "" - return 1 - fi - fi - - # Receipt is null - transaction not yet mined - if [ $attempt -lt $max_attempts ]; then - print_info "Transaction pending, waiting for confirmation... ($attempt/$max_attempts)" - sleep 2 - fi - done - - print_error "Transaction not confirmed on Sepolia after $max_attempts attempts" - TEST_RESULTS[$TOTAL_TESTS]="FAILED" - FAILED_TESTS=$((FAILED_TESTS + 1)) - TOTAL_TESTS=$((TOTAL_TESTS + 1)) - echo "" - return 1 -} - -# Main test flow -main() { - print_header "BLOCKCHAIN TRANSACTION API TEST SUITE" - - # Test 1: Basic Transfers - print_header "1. BASIC TRANSFERS" - - TX_HASH1=$(generate_tx_hash) - api_call "sendTransaction" "{\"sender\":\"alice\",\"receiver\":\"bob\",\"value\":1000,\"token\":\"USDT\",\"hash\":\"$TX_HASH1\"}" "Alice transfers 1000 USDT to Bob" - - TX_HASH2=$(generate_tx_hash) - api_call "sendTransaction" "{\"sender\":\"bob\",\"receiver\":\"charlie\",\"value\":500,\"token\":\"USDT\",\"hash\":\"$TX_HASH2\"}" "Bob transfers 500 USDT to Charlie" - - TX_HASH3=$(generate_tx_hash) - api_call "sendTransaction" "{\"sender\":\"alice\",\"receiver\":\"charlie\",\"value\":1,\"token\":\"BTC\",\"hash\":\"$TX_HASH3\"}" "Alice transfers 1 BTC to Charlie" - - # Test 2: Check Transaction Status - print_header "2. CHECKING TRANSACTION STATUS" - - check_tx_status "$TX_HASH1" - check_tx_status "$TX_HASH2" - check_tx_status "$TX_HASH3" - - # Test 3: Error Cases - print_header "3. ERROR HANDLING TESTS" - - ERROR_TX=$(generate_tx_hash) - api_call "sendTransaction" "{\"sender\":\"nonexistent\",\"receiver\":\"alice\",\"value\":1000,\"token\":\"USDT\",\"hash\":\"$ERROR_TX\"}" "Transfer from non-existent account (should fail)" - - # Check Alice's current balance and send more than that - ALICE_CURRENT_BALANCE=$(check_balance "alice" "USDT" "Alice's current balance for insufficient funds test") - # Send current balance + 10000 to ensure it exceeds available balance - INSUFFICIENT_AMOUNT=$((ALICE_CURRENT_BALANCE + 10000)) - - ERROR_TX2=$(generate_tx_hash) - api_call "sendTransaction" "{\"sender\":\"alice\",\"receiver\":\"bob\",\"value\":$INSUFFICIENT_AMOUNT,\"token\":\"USDT\",\"hash\":\"$ERROR_TX2\"}" "Transfer more than available balance (should fail)" - - # Test 4: Check Error Transaction Status - print_header "4. CHECKING ERROR TRANSACTION STATUS" - - check_tx_status "$ERROR_TX" - check_tx_status "$ERROR_TX2" - - # Test 5: External Transaction Generation (only 1 to save Sepolia balance) - print_header "5. EXTERNAL TRANSACTION TEST" - - # Get current count of external transactions before sending - INITIAL_EXT_COUNT=$(curl -s -X POST "$PELACLI_API_URL" -H "$CONTENT_TYPE" \ - -d '{"jsonrpc":"2.0","method":"ext_listTransactions","params":[{"fromChainId":42,"limit":1}],"id":0}' \ - | jq -r '.result.total // 0' 2>/dev/null) - print_info "Initial external tx count: $INITIAL_EXT_COUNT" - - EXT_TX1=$(generate_tx_hash) - # Use random value (100-999) to ensure unique payload on Sepolia each run - EXT_VALUE=$((100 + RANDOM % 900)) - api_call "sendTransaction" "{\"sender\":\"alice\",\"receiver\":\"bob\",\"value\":$EXT_VALUE,\"token\":\"USDT\",\"hash\":\"$EXT_TX1\",\"generate_ext_txn\":true}" "Alice transfer $EXT_VALUE USDT with external txn" - - # Test 6: Check External Transaction Status on Appchain - print_header "6. CHECKING EXTERNAL TRANSACTION STATUS (APPCHAIN)" - - check_tx_status "$EXT_TX1" - - # Test 7: Get external tx hash from pelacli and verify on Sepolia - print_header "7. VERIFYING EXTERNAL TRANSACTION ON SEPOLIA" - - # Wait for pelacli to process and get the Sepolia tx hash - EXPECTED_COUNT=$((INITIAL_EXT_COUNT + 1)) - print_info "Waiting for external tx to appear in pelacli (expecting count >= $EXPECTED_COUNT)..." - - SEPOLIA_TX_HASH=$(get_latest_external_txn $EXPECTED_COUNT) - if [ -z "$SEPOLIA_TX_HASH" ]; then - print_error "Failed to get external transaction hash from pelacli" - TEST_METHODS[$TOTAL_TESTS]="ext_listTransactions" - TEST_DESCRIPTIONS[$TOTAL_TESTS]="Get external tx hash from pelacli" - TEST_RESULTS[$TOTAL_TESTS]="FAILED" - FAILED_TESTS=$((FAILED_TESTS + 1)) - TOTAL_TESTS=$((TOTAL_TESTS + 1)) - else - print_success "Got Sepolia tx hash: $SEPOLIA_TX_HASH" - TEST_METHODS[$TOTAL_TESTS]="ext_listTransactions" - TEST_DESCRIPTIONS[$TOTAL_TESTS]="Get external tx hash from pelacli" - TEST_RESULTS[$TOTAL_TESTS]="SUCCESS" - SUCCESSFUL_TESTS=$((SUCCESSFUL_TESTS + 1)) - TOTAL_TESTS=$((TOTAL_TESTS + 1)) - - # Verify the transaction on Sepolia - verify_tx_on_sepolia "$SEPOLIA_TX_HASH" "Verify tx confirmed on Sepolia" - fi - - # Summary - print_header "TEST SUITE COMPLETED" - print_success "Basic transfer, error handling, and external transaction tests completed!" - print_info "Check the responses above for any errors or unexpected results" -} - -# Check if jq is installed for pretty printing -if ! command -v jq &> /dev/null; then - print_error "jq is not installed. Install it for better JSON formatting:" - echo " brew install jq (on macOS)" - echo " apt-get install jq (on Ubuntu/Debian)" - echo "" - print_info "Continuing without JSON formatting..." -fi - -# Check if the appchain API is running -print_info "Checking if appchain API is running at $API_URL..." -if curl -s -f -X POST "$API_URL" -H "$CONTENT_TYPE" -d '{"jsonrpc":"2.0","method":"getTransactionStatus","params":["0x0"],"id":0}' > /dev/null 2>&1; then - print_success "Appchain API is running!" -else - print_error "Appchain API is not responding at $API_URL" - print_error "Please start the appchain server first" - exit 1 -fi - -# Check if the pelacli API is running (don't use -f since error responses are valid) -print_info "Checking if pelacli API is running at $PELACLI_API_URL..." -PELACLI_RESPONSE=$(curl -s -X POST "$PELACLI_API_URL" -H "$CONTENT_TYPE" -d '{"jsonrpc":"2.0","method":"ext_getTransaction","params":["0x0"],"id":0}' 2>&1) -if [ $? -eq 0 ] && echo "$PELACLI_RESPONSE" | grep -q "jsonrpc"; then - print_success "Pelacli API is running!" -else - print_error "Pelacli API is not responding at $PELACLI_API_URL" - print_error "Please start the pelacli server first" - exit 1 -fi - -# Run the main test suite -main - -# Print test summary -print_header "TEST SUMMARY" - -echo -e "\n${YELLOW}Test Results:${NC}" -echo -e "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo -e "${BLUE}Total Tests:${NC} $TOTAL_TESTS" -echo -e "${GREEN}Successful:${NC} $SUCCESSFUL_TESTS" -echo -e "${RED}Failed:${NC} $FAILED_TESTS" -echo -e "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - -# Show detailed results if there are failures -if [ $FAILED_TESTS -gt 0 ]; then - echo -e "\n${RED}Failed Tests:${NC}" - for i in "${!TEST_RESULTS[@]}"; do - if [ "${TEST_RESULTS[$i]}" == "FAILED" ]; then - echo -e " ${RED}βœ—${NC} Test #$((i+1)): ${TEST_METHODS[$i]} - ${TEST_DESCRIPTIONS[$i]}" - fi - done -fi - -# Show all test results in a table format -echo -e "\n${YELLOW}Detailed Test Results:${NC}" -echo -e "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -printf "%-5s %-25s %-60s %s\n" "ID" "Method" "Description" "Result" -echo -e "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - -for i in "${!TEST_RESULTS[@]}"; do - case "${TEST_RESULTS[$i]}" in - "SUCCESS") - result_color="${GREEN}βœ“ SUCCESS${NC}" - ;; - "SUCCESS (Validation)") - result_color="${GREEN}βœ“ SUCCESS (Validation)${NC}" - ;; - "FAILED") - result_color="${RED}βœ— FAILED${NC}" - ;; - "PENDING") - result_color="${YELLOW}PENDING${NC}" - ;; - "") - result_color="${BLUE}SKIPPED${NC}" - ;; - *) - result_color="${BLUE}${TEST_RESULTS[$i]}${NC}" - ;; - esac - printf "%-5s %-25s %-60s " "$((i+1))" "${TEST_METHODS[$i]}" "${TEST_DESCRIPTIONS[$i]:0:60}" - echo -e "$result_color" -done - -echo -e "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - -# Final status -echo "" -if [ $FAILED_TESTS -eq 0 ]; then - print_success "All tests passed successfully! πŸŽ‰" - print_header "END OF TEST SUITE" - exit 0 -else - print_error "$FAILED_TESTS test(s) failed. Please review the results above." - print_header "END OF TEST SUITE" - exit 1 -fi