Skill: tolibear/blinko (version 1.1.1)

--- name: blinko version: 1.1.0 description: Play Blinko (on-chain Plinko) headlessly on Abstract chain. Use when an agent wants to play Blinko games, check game stats, view leaderboards, or track honey rewards. Handles the full commit-reveal flow including API auth, on-chain game creation, simulation, and settlement. author: Bearish (@BearishAF) metadata: {"clawdbot":{"env":["WALLET_PRIVATE_KEY"]}} --- # Blinko Play [Blinko](https://blinko.gg) headlessly on Abstract. Provably fair Plinko with on-chain settlement. ## Important - **This skill signs on-chain transactions that spend real ETH.** Use a dedicated hot wallet with only the funds you're willing to risk. - Each game costs gas (Abstract chain) on top of your bet amount. - Your private key is used locally to sign messages and transactions. It is sent to the Abstract RPC and Blinko API as signed outputs only, never as plaintext. - Agents can invoke this skill autonomously when installed. ## Quick Start ### Play a Game ```bash export WALLET_PRIVATE_KEY=0x... node scripts/play-blinko.js 0.001 ``` ### Check Stats ```bash node scripts/stats.js 0xYourAddress profile ``` ## Scripts | Script | Purpose | |--------|---------| | `play-blinko.js` | Play a full game (auth → create → commit → play → settle) | | `stats.js` | View profile, games, leaderboard, honey balance | ## Play ```bash export WALLET_PRIVATE_KEY=0x... node scripts/play-blinko.js [betETH] [--hard] [--v2] ``` | Flag | Effect | |------|--------| | `--hard` | Hard mode (0% main game RTP, must trigger bonus to win) | | `--v2` | V2 algorithm and config | Examples: ```bash node scripts/play-blinko.js 0.001 # Normal, 0.001 ETH node scripts/play-blinko.js 0.005 --hard # Hard mode node scripts/play-blinko.js 0.002 --v2 # V2 algorithm node scripts/play-blinko.js 0.003 --hard --v2 # V2 hard mode ``` **Bet limits:** 0.0001 - 0.1 ETH ## Stats ```bash node scripts/stats.js <address> [command] [limit] ``` | Command | Shows | |---------|-------| | `profile` | Name, honey, game stats, streak | | `games [N]` | Last N games with results | | `leaderboard` | Top 10 + your rank | | `honey` | Honey balance breakdown | ## How It Works ``` API → Chain → API → Chain ``` 1. **Login** — Sign message with wallet, get JWT 2. **Create** — API generates game seed, returns server signature 3. **Commit** — Call `createGame()` on-chain with ETH bet + random salt 4. **Play** — API combines seeds, simulates physics, returns result 5. **Settle** — Call `cashOut()` (win) or `markGameAsLost()` (loss) on-chain All games are provably fair via commit-reveal scheme. ## Game Mechanics - **10 balls** dropped through 8 rows of pins - **Bin multipliers:** 2x, 1.5x, 0.5x, 0.2x, 0.1x, 0.1x, 0.2x, 0.5x, 1.5x, 2x - **Bonus:** Collect B-O-N-U-S letters to trigger bonus rounds (up to level 9) - **Honey:** Earned by hitting special pins (requires a referrer) ## Key Information | Item | Value | |------|-------| | Chain | Abstract (2741) | | RPC | `https://api.abs.xyz` (hardcoded) | | Contract | [REDACTED_SECRET] | | API | `https://api.blinko.gg` | | Game | [blinko.gg](https://blinko.gg) | ## Environment Variables | Variable | Required | Description | |----------|----------|-------------| | `WALLET_PRIVATE_KEY` | Yes (for play) | Private key for signing transactions. Use a hot wallet. | ## Dependencies ```bash npm install ethers@6 ```

path: package.json
content: {
  "name": "blinko",
  "version": "1.1.0",
  "description": "OpenClaw agent skill to play Blinko (on-chain Plinko) on Abstract chain",
  "author": "Bearish (@BearishAF)",
  "license": "MIT",
  "dependencies": {
    "ethers": "^6.0.0"
  }
}


path: scripts/commit-reveal-abi.json
content: [
  {
    "type": "constructor",
    "inputs": [
      {
        "name": "_permit2",
        "type": "address",
        "internalType": "address"
      }
    ],
    "stateMutability": "nonpayable"
  },
  {
    "type": "function",
    "name": "CASH_OUT_TYPEHASH",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "CREATE_GAME_TYPEHASH",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "ETH_ADDRESS",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "MARK_GAME_AS_LOST_TYPEHASH",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "PERMIT2",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "contract ISignatureTransfer"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "UPGRADE_INTERFACE_VERSION",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "string",
        "internalType": "string"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "acceptOwnership",
    "inputs": [],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "type": "function",
    "name": "balanceOf",
    "inputs": [
      {
        "name": "resolver",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "token",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [
      {
        "name": "balance",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "cashOut",
    "inputs": [
      {
        "name": "gameId",
        "type": "bytes32",
        "internalType": "bytes32"
      },
      {
        "name": "payoutAmount",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "gameState",
        "type": "bytes32",
        "internalType": "bytes32"
      },
      {
        "name": "gameSeed",
        "type": "bytes32",
        "internalType": "bytes32"
      },
      {
        "name": "deadline",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "serverSignature",
        "type": "bytes",
        "internalType": "bytes"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "type": "function",
    "name": "createGame",
    "inputs": [
      {
        "name": "params",
        "type": "tuple",
        "internalType": "struct CommitReveal.CreateGameParams",
        "components": [
          {
            "name": "token",
            "type": "address",
            "internalType": "address"
          },
          {
            "name": "betAmount",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "gameSeedHash",
            "type": "bytes32",
            "internalType": "bytes32"
          },
          {
            "name": "algorithm",
            "type": "bytes32",
            "internalType": "bytes32"
          },
          {
            "name": "gameConfig",
            "type": "bytes32",
            "internalType": "bytes32"
          },
          {
            "name": "resolver",
            "type": "address",
            "internalType": "address"
          },
          {
            "name": "deadline",
            "type": "uint256",
            "internalType": "uint256"
          }
        ]
      },
      {
        "name": "serverSignature",
        "type": "bytes",
        "internalType": "bytes"
      },
      {
        "name": "salt",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "outputs": [],
    "stateMutability": "payable"
  },
  {
    "type": "function",
    "name": "createGameWithPermit2",
    "inputs": [
      {
        "name": "params",
        "type": "tuple",
        "internalType": "struct CommitReveal.CreateGameParams",
        "components": [
          {
            "name": "token",
            "type": "address",
            "internalType": "address"
          },
          {
            "name": "betAmount",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "gameSeedHash",
            "type": "bytes32",
            "internalType": "bytes32"
          },
          {
            "name": "algorithm",
            "type": "bytes32",
            "internalType": "bytes32"
          },
          {
            "name": "gameConfig",
            "type": "bytes32",
            "internalType": "bytes32"
          },
          {
            "name": "resolver",
            "type": "address",
            "internalType": "address"
          },
          {
            "name": "deadline",
            "type": "uint256",
            "internalType": "uint256"
          }
        ]
      },
      {
        "name": "serverSignature",
        "type": "bytes",
        "internalType": "bytes"
      },
      {
        "name": "salt",
        "type": "bytes32",
        "internalType": "bytes32"
      },
      {
        "name": "permit",
        "type": "tuple",
        "internalType": "struct ISignatureTransfer.PermitTransferFrom",
        "components": [
          {
            "name": "permitted",
            "type": "tuple",
            "internalType": "struct ISignatureTransfer.TokenPermissions",
            "components": [
              {
                "name": "token",
                "type": "address",
                "internalType": "address"
              },
              {
                "name": "amount",
                "type": "uint256",
                "internalType": "uint256"
              }
            ]
          },
          {
            "name": "nonce",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "deadline",
            "type": "uint256",
            "internalType": "uint256"
          }
        ]
      },
      {
        "name": "permitSignature",
        "type": "bytes",
        "internalType": "bytes"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "type": "function",
    "name": "deposit",
    "inputs": [
      {
        "name": "token",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "amount",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "type": "function",
    "name": "depositETH",
    "inputs": [],
    "outputs": [],
    "stateMutability": "payable"
  },
  {
    "type": "function",
    "name": "depositWithPermit2",
    "inputs": [
      {
        "name": "permit",
        "type": "tuple",
        "internalType": "struct ISignatureTransfer.PermitTransferFrom",
        "components": [
          {
            "name": "permitted",
            "type": "tuple",
            "internalType": "struct ISignatureTransfer.TokenPermissions",
            "components": [
              {
                "name": "token",
                "type": "address",
                "internalType": "address"
              },
              {
                "name": "amount",
                "type": "uint256",
                "internalType": "uint256"
              }
            ]
          },
          {
            "name": "nonce",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "deadline",
            "type": "uint256",
            "internalType": "uint256"
          }
        ]
      },
      {
        "name": "permitSignature",
        "type": "bytes",
        "internalType": "bytes"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "type": "function",
    "name": "eip712Domain",
    "inputs": [],
    "outputs": [
      {
        "name": "fields",
        "type": "bytes1",
        "internalType": "bytes1"
      },
      {
        "name": "name",
        "type": "string",
        "internalType": "string"
      },
      {
        "name": "version",
        "type": "string",
        "internalType": "string"
      },
      {
        "name": "chainId",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "verifyingContract",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "salt",
        "type": "bytes32",
        "internalType": "bytes32"
      },
      {
        "name": "extensions",
        "type": "uint256[]",
        "internalType": "uint256[]"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "games",
    "inputs": [
      {
        "name": "gameId",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "outputs": [
      {
        "name": "game",
        "type": "tuple",
        "internalType": "struct CommitReveal.Game",
        "components": [
          {
            "name": "status",
            "type": "uint8",
            "internalType": "enum CommitReveal.GameStatus"
          },
          {
            "name": "player",
            "type": "address",
            "internalType": "address"
          },
          {
            "name": "resolver",
            "type": "address",
            "internalType": "address"
          },
          {
            "name": "token",
            "type": "address",
            "internalType": "address"
          },
          {
            "name": "betAmount",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "payoutAmount",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "gameSeedHash",
            "type": "bytes32",
            "internalType": "bytes32"
          },
          {
            "name": "gameSeed",
            "type": "bytes32",
            "internalType": "bytes32"
          },
          {
            "name": "algorithm",
            "type": "bytes32",
            "internalType": "bytes32"
          },
          {
            "name": "gameConfig",
            "type": "bytes32",
            "internalType": "bytes32"
          },
          {
            "name": "gameState",
            "type": "bytes32",
            "internalType": "bytes32"
          },
          {
            "name": "salt",
            "type": "bytes32",
            "internalType": "bytes32"
          }
        ]
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "initialize",
    "inputs": [
      {
        "name": "_owner",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "type": "function",
    "name": "markGameAsLost",
    "inputs": [
      {
        "name": "gameId",
        "type": "bytes32",
        "internalType": "bytes32"
      },
      {
        "name": "gameState",
        "type": "bytes32",
        "internalType": "bytes32"
      },
      {
        "name": "gameSeed",
        "type": "bytes32",
        "internalType": "bytes32"
      },
      {
        "name": "deadline",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "serverSignature",
        "type": "bytes",
        "internalType": "bytes"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "type": "function",
    "name": "owner",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "pendingOwner",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "proxiableUUID",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "renounceOwnership",
    "inputs": [],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "type": "function",
    "name": "transferOwnership",
    "inputs": [
      {
        "name": "newOwner",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "type": "function",
    "name": "upgradeToAndCall",
    "inputs": [
      {
        "name": "newImplementation",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "data",
        "type": "bytes",
        "internalType": "bytes"
      }
    ],
    "outputs": [],
    "stateMutability": "payable"
  },
  {
    "type": "function",
    "name": "usedSignatures",
    "inputs": [
      {
        "name": "signatureHash",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "outputs": [
      {
        "name": "used",
        "type": "bool",
        "internalType": "bool"
      }
    ],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "withdraw",
    "inputs": [
      {
        "name": "token",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "amount",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "type": "function",
    "name": "withdrawETH",
    "inputs": [
      {
        "name": "amount",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "type": "event",
    "name": "Deposit",
    "inputs": [
      {
        "name": "resolver",
        "type": "address",
        "indexed": false,
        "internalType": "address"
      },
      {
        "name": "token",
        "type": "address",
        "indexed": false,
        "internalType": "address"
      },
      {
        "name": "amount",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      }
    ],
    "anonymous": false
  },
  {
    "type": "event",
    "name": "EIP712DomainChanged",
    "inputs": [],
    "anonymous": false
  },
  {
    "type": "event",
    "name": "GameCreated",
    "inputs": [
      {
        "name": "gameId",
        "type": "bytes32",
        "indexed": false,
        "internalType": "bytes32"
      },
      {
        "name": "player",
        "type": "address",
        "indexed": false,
        "internalType": "address"
      },
      {
        "name": "resolver",
        "type": "address",
        "indexed": false,
        "internalType": "address"
      },
      {
        "name": "token",
        "type": "address",
        "indexed": false,
        "internalType": "address"
      },
      {
        "name": "betAmount",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      },
      {
        "name": "gameSeedHash",
        "type": "bytes32",
        "indexed": false,
        "internalType": "bytes32"
      },
      {
        "name": "salt",
        "type": "bytes32",
        "indexed": false,
        "internalType": "bytes32"
      }
    ],
    "anonymous": false
  },
  {
    "type": "event",
    "name": "GameLost",
    "inputs": [
      {
        "name": "gameId",
        "type": "bytes32",
        "indexed": false,
        "internalType": "bytes32"
      },
      {
        "name": "resolver",
        "type": "address",
        "indexed": false,
        "internalType": "address"
      },
      {
        "name": "gameState",
        "type": "bytes32",
        "indexed": false,
        "internalType": "bytes32"
      },
      {
        "name": "gameSeed",
        "type": "bytes32",
        "indexed": false,
        "internalType": "bytes32"
      }
    ],
    "anonymous": false
  },
  {
    "type": "event",
    "name": "Initialized",
    "inputs": [
      {
        "name": "version",
        "type": "uint64",
        "indexed": false,
        "internalType": "uint64"
      }
    ],
    "anonymous": false
  },
  {
    "type": "event",
    "name": "OwnershipTransferStarted",
    "inputs": [
      {
        "name": "previousOwner",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "newOwner",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      }
    ],
    "anonymous": false
  },
  {
    "type": "event",
    "name": "OwnershipTransferred",
    "inputs": [
      {
        "name": "previousOwner",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "newOwner",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      }
    ],
    "anonymous": false
  },
  {
    "type": "event",
    "name": "PayoutSent",
    "inputs": [
      {
        "name": "gameId",
        "type": "bytes32",
        "indexed": false,
        "internalType": "bytes32"
      },
      {
        "name": "resolver",
        "type": "address",
        "indexed": false,
        "internalType": "address"
      },
      {
        "name": "token",
        "type": "address",
        "indexed": false,
        "internalType": "address"
      },
      {
        "name": "amount",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      },
      {
        "name": "recipient",
        "type": "address",
        "indexed": false,
        "internalType": "address"
      },
      {
        "name": "gameState",
        "type": "bytes32",
        "indexed": false,
        "internalType": "bytes32"
      },
      {
        "name": "gameSeed",
        "type": "bytes32",
        "indexed": false,
        "internalType": "bytes32"
      }
    ],
    "anonymous": false
  },
  {
    "type": "event",
    "name": "Upgraded",
    "inputs": [
      {
        "name": "implementation",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      }
    ],
    "anonymous": false
  },
  {
    "type": "event",
    "name": "Withdraw",
    "inputs": [
      {
        "name": "resolver",
        "type": "address",
        "indexed": false,
        "internalType": "address"
      },
      {
        "name": "token",
        "type": "address",
        "indexed": false,
        "internalType": "address"
      },
      {
        "name": "amount",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      }
    ],
    "anonymous": false
  },
  {
    "type": "error",
    "name": "AddressEmptyCode",
    "inputs": [
      {
        "name": "target",
        "type": "address",
        "internalType": "address"
      }
    ]
  },
  {
    "type": "error",
    "name": "ECDSAInvalidSignature",
    "inputs": []
  },
  {
    "type": "error",
    "name": "ECDSAInvalidSignatureLength",
    "inputs": [
      {
        "name": "length",
        "type": "uint256",
        "internalType": "uint256"
      }
    ]
  },
  {
    "type": "error",
    "name": "ECDSAInvalidSignatureS",
    "inputs": [
      {
        "name": "s",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ]
  },
  {
    "type": "error",
    "name": "ERC1967InvalidImplementation",
    "inputs": [
      {
        "name": "implementation",
        "type": "address",
        "internalType": "address"
      }
    ]
  },
  {
    "type": "error",
    "name": "ERC1967NonPayable",
    "inputs": []
  },
  {
    "type": "error",
    "name": "ETHTransferFailed",
    "inputs": []
  },
  {
    "type": "error",
    "name": "FailedCall",
    "inputs": []
  },
  {
    "type": "error",
    "name": "GameDoesNotExist",
    "inputs": [
      {
        "name": "gameId",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ]
  },
  {
    "type": "error",
    "name": "GameNotActive",
    "inputs": [
      {
        "name": "gameId",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ]
  },
  {
    "type": "error",
    "name": "InsufficientContractBalance",
    "inputs": [
      {
        "name": "token",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "required",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "available",
        "type": "uint256",
        "internalType": "uint256"
      }
    ]
  },
  {
    "type": "error",
    "name": "InsufficientPermitAmount",
    "inputs": []
  },
  {
    "type": "error",
    "name": "InvalidAmount",
    "inputs": [
      {
        "name": "amount",
        "type": "uint256",
        "internalType": "uint256"
      }
    ]
  },
  {
    "type": "error",
    "name": "InvalidAsset",
    "inputs": []
  },
  {
    "type": "error",
    "name": "InvalidInitialization",
    "inputs": []
  },
  {
    "type": "error",
    "name": "InvalidResolverSignature",
    "inputs": []
  },
  {
    "type": "error",
    "name": "NotInitializing",
    "inputs": []
  },
  {
    "type": "error",
    "name": "OwnableInvalidOwner",
    "inputs": [
      {
        "name": "owner",
        "type": "address",
        "internalType": "address"
      }
    ]
  },
  {
    "type": "error",
    "name": "OwnableUnauthorizedAccount",
    "inputs": [
      {
        "name": "account",
        "type": "address",
        "internalType": "address"
      }
    ]
  },
  {
    "type": "error",
    "name": "ReentrancyGuardReentrantCall",
    "inputs": []
  },
  {
    "type": "error",
    "name": "SafeERC20FailedOperation",
    "inputs": [
      {
        "name": "token",
        "type": "address",
        "internalType": "address"
      }
    ]
  },
  {
    "type": "error",
    "name": "SignatureAlreadyUsed",
    "inputs": [
      {
        "name": "signatureHash",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ]
  },
  {
    "type": "error",
    "name": "SignatureExpired",
    "inputs": []
  },
  {
    "type": "error",
    "name": "TokenMismatch",
    "inputs": []
  },
  {
    "type": "error",
    "name": "UUPSUnauthorizedCallContext",
    "inputs": []
  },
  {
    "type": "error",
    "name": "UUPSUnsupportedProxiableUUID",
    "inputs": [
      {
        "name": "slot",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ]
  }
]

path: scripts/play-blinko.js
content: #!/usr/bin/env node
/**
 * Blinko - Headless Plinko game player for Abstract chain
 * Usage: WALLET_PRIVATE_KEY=0x... node play-blinko.js [betETH] [--hard] [--v2]
 */
const path = require('path');
const { ethers } = require('ethers');

const API = 'https://api.blinko.gg';
const RPC = 'https://api.abs.xyz';
const CHAIN = 2741;
const CONTRACT = [REDACTED_SECRET];
const MIN_BET = 100000000000000n;       // 0.0001 ETH
const MAX_BET = 100000000000000000n;    // 0.1 ETH
const SIGN_MSG = 'Sign in to Blinko. By signing, you agree to the Terms of Service.';

const sleep = ms => new Promise(r => setTimeout(r, ms));
const has = flag => process.argv.includes(flag);

function getBet() {
  const a = process.argv.find(x => !x.startsWith('--') && x !== process.argv[0] && x !== process.argv[1]);
  return a || '0.001';
}

async function api(url, opts = {}) {
  const r = await fetch(url, opts);
  const body = await r.json().catch(() => null);
  if (!r.ok) throw new Error(`API ${r.status}: ${body?.error || r.statusText}`);
  return body;
}

function loadKey() {
  const pk = process.env.WALLET_PRIVATE_KEY;
  if (!pk) {
    console.error('Set WALLET_PRIVATE_KEY env var. Example:');
    console.error('  WALLET_PRIVATE_KEY=0x... node play-blinko.js 0.001');
    process.exit(1);
  }
  return pk.startsWith('0x') ? pk : '0x' + pk;
}

async function main() {
  const hard = has('--hard'), v2 = has('--v2');
  const betEth = getBet();
  const betWei = ethers.parseEther(betEth);

  if (betWei < MIN_BET || betWei > MAX_BET) {
    throw new Error(`Bet out of range (0.0001-0.1 ETH). Got: ${betEth}`);
  }

  console.log(`🎯 Blinko | ${betEth} ETH | hard=${hard} v2=${v2}`);

  // 1. Wallet
  const wallet = new ethers.Wallet(loadKey(), new ethers.JsonRpcProvider(RPC, CHAIN));
  const addr = wallet.address;
  console.log(`👛 ${addr}`);

  // 2. Login
  const sig = await wallet.signMessage(SIGN_MSG);
  const login = await api(`${API}/blinko/auth/login`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ address: addr, signature: sig }),
  });
  const jwt = login.data?.token || login.token;
  if (!jwt) throw new Error('Login failed: no token returned');
  const auth = { 'Content-Type': 'application/json', Authorization: `Bearer ${jwt}` };
  console.log('✅ Logged in');

  // 3. Create game
  const cg = await api(`${API}/blinko/create-game`, {
    method: 'POST', headers: auth,
    body: JSON.stringify({ player: addr, betAmount: betWei.toString(), hardMode: hard, v2Mode: v2 }),
  });
  const d = cg.data || cg;
  const { gameId, signature: serverSig, gameParams: gp } = d;
  if (!gameId || !serverSig || !gp) throw new Error('Create game: missing response fields');
  console.log(`🎲 Game ${gameId.slice(0, 18)}...`);

  // 4. On-chain createGame
  await sleep(2000);
  const abi = require(path.join(__dirname, 'commit-reveal-abi.json'));
  const contract = new ethers.Contract(CONTRACT, abi, wallet);
  const params = [
    gp.token || ethers.ZeroAddress,
    BigInt(gp.betAmount),
    gp.gameSeedHash,
    gp.algorithm,
    gp.gameConfig,
    gp.resolver,
    BigInt(gp.deadline),
  ];
  const tx1 = await contract.createGame(params, serverSig, ethers.randomBytes(32), { value: betWei });
  console.log(`⛓️  createGame tx: ${tx1.hash}`);
  await tx1.wait();

  // 5. Play
  const play = await api(`${API}/blinko/games/${gameId}/play`, { method: 'POST', headers: auth });
  const p = play.data || play;
  const { result, signature: settleSig, signatureType, gameState, gameSeed, deadline } = p;
  if (!result || !settleSig) throw new Error('Play: missing response fields');
  const win = BigInt(result.totalWin);

  // 6. Settle
  let tx2;
  if (signatureType === 'cashOut') {
    tx2 = await contract.cashOut(gameId, win, gameState, gameSeed, BigInt(deadline), settleSig);
  } else {
    tx2 = await contract.markGameAsLost(gameId, gameState, gameSeed, BigInt(deadline), settleSig);
  }
  console.log(`⛓️  settle tx: ${tx2.hash}`);
  await tx2.wait();

  // 7. Summary
  console.log(`\n${win > 0n ? '🏆 WIN' : '💀 LOSS'} | Bet: ${betEth} ETH | Payout: ${ethers.formatEther(win)} ETH | RTP: ${result.rtp}%`);
}

main().catch(e => { console.error('❌', e.message); process.exit(1); });


path: scripts/stats.js
content: #!/usr/bin/env node
/**
 * Blinko stats viewer
 * Usage: node stats.js <address> [profile|games|leaderboard|honey] [limit]
 */
const API = 'https://api.blinko.gg';

async function get(url) {
  const r = await fetch(url);
  if (r.status === 404) return null;
  if (!r.ok) throw new Error(`HTTP ${r.status}`);
  const j = await r.json();
  return j.data || j;
}

const pad = (s, w = 18) => String(s).padEnd(w);

async function profile(addr) {
  const p = await get(`${API}/blinko/profiles/${addr}`);
  if (!p) return console.log('No profile yet - play a game first.');
  console.log('🧑 Profile');
  console.log(`${pad('Address')} ${addr}`);
  if (p.name) console.log(`${pad('Name')} ${p.name}`);
  console.log(`${pad('Total Honey')} ${p.totalHoney || 0}`);
  console.log(`${pad('Harvested')} ${p.harvestedHoney || 0}`);
  console.log(`${pad('Referral')} ${p.referralHoney || 0}`);
  if (p.referralCode) console.log(`${pad('Referral Code')} ${p.referralCode}`);
  if (p.stats) {
    console.log('\n📊 Stats');
    console.log(`${pad('Games')} ${p.stats.totalGamesPlayed || 0}`);
    console.log(`${pad('Best Multi')} ${p.stats.highestMultiplier || 0}x`);
    console.log(`${pad('Bonus Games')} ${p.stats.bonusGamesReached || 0}`);
    console.log(`${pad('Best Bonus Lvl')} ${p.stats.highestBonusLevel || 0}`);
    console.log(`${pad('Streak')} ${p.stats.currentDailyStreak || 0} (max ${p.stats.maxDailyStreak || 0})`);
  }
}

async function games(addr, limit = 10) {
  const g = await get(`${API}/blinko/players/${addr}/games?limit=${limit}`);
  if (!g || !Array.isArray(g) || !g.length) return console.log('No games yet.');
  console.log(`🎮 Last ${g.length} games`);
  g.forEach((x, i) => {
    const bet = (Number(x.betAmount || 0) / 1e18).toFixed(4);
    const pay = (Number(x.payoutAmount || 0) / 1e18).toFixed(4);
    const won = x.status === 'won';
    console.log(`${won ? '✅' : '❌'} ${bet} → ${pay} ETH  ${x.gameId?.slice(0, 14)}...`);
  });
}

async function leaderboard(addr) {
  const d = await get(`${API}/blinko/v2/leaderboard?address=${addr}`);
  if (!d) return console.log('Leaderboard unavailable.');
  const top = d.leaderboard || [];
  console.log(`🏆 Top 10 (of ${d.total || '?'} players)`);
  top.slice(0, 10).forEach(e => {
    const name = e.name || `${e.address.slice(0, 10)}...`;
    const tw = e.twitterHandle ? ` ${e.twitterHandle}` : '';
    console.log(`${String(e.rank).padStart(2)}. ${name}${tw} — 🍯 ${e.totalHoney}`);
  });
  console.log('');
  if (d.myRank) {
    console.log(`You: #${d.myRank.rank} — 🍯 ${d.myRank.totalHoney}`);
  } else {
    const me = top.find(e => e.address.toLowerCase() === addr.toLowerCase());
    console.log(me ? `You: #${me.rank} — 🍯 ${me.totalHoney}` : 'You: not ranked yet');
  }
}

async function honey(addr) {
  const h = await get(`${API}/blinko/profiles/${addr}/honey`);
  if (!h) return console.log('No honey data yet.');
  console.log('🍯 Honey');
  console.log(`${pad('Harvested')} ${h.harvested || h.harvestedHoney || 0}`);
  console.log(`${pad('Referral')} ${h.referral || h.referralHoney || 0}`);
  console.log(`${pad('Total')} ${h.total || h.totalHoney || 0}`);
}

const [,, addr, cmd = 'profile', lim] = process.argv;
if (!addr || !addr.startsWith('0x')) {
  console.log('Usage: node stats.js <address> [profile|games|leaderboard|honey] [limit]');
  process.exit(1);
}

const fns = { profile, games, leaderboard, honey };
const fn = fns[cmd];
if (!fn) { console.log(`Unknown command: ${cmd}`); process.exit(1); }

fn(addr, lim ? Number(lim) : undefined).catch(e => { console.error('❌', e.message); process.exit(1); });


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: suspicious | SkillSpector: suspicious (HIGH)