ospex-one

GitHub 作者 LeoYeAI/openclaw-master-skills v1.7.1

Bet on sports with one word (or maybe, a few words). Say a team name, city, or abbreviation. 'Edmonton', 'Duke', 'Celtics', 'Lakers'. NBA, NHL, NCAAB.

安装 / 下载方式

TotalClaw CLI推荐
totalclaw install github:LeoYeAI~openclaw-master-skills~ospex-one
cURL直接下载,无需登录
curl -fsSL https://skills.taituai.com/api/skills/github%3ALeoYeAI~openclaw-master-skills~ospex-one/file -o ospex-one.md
# ospex-one

One word in, one transaction hash out. This skill emphasizes execution over discussion and is low-risk by design.

**Invocation:** This skill is **invocation-only**: the agent will not use it unless you explicitly ask or reference a team name in context. That avoids accidental bets.

**Trust & safety:** This skill executes real transactions on Polygon mainnet using your configured wallet. Positions are capped at 3 USDC. Unmatched position amounts can be withdrawn at any time. The agent verifies all transaction parameters against hardcoded contract addresses and expected method names before signing — if anything is unexpected, it halts and reports rather than proceeding. OSPEX_WALLET_PRIVATE_KEY is high-sensitivity: the agent must never log, echo, or expose it. Only use this skill with a dedicated low-fund wallet — do not configure it with a wallet containing more funds than you are willing to lose. Full risk assessment: https://github.com/ospex-org/ospex-contracts-v2/blob/main/docs/RISKS.md

**Input expectations:** This skill is designed for single-word input — a team name, city, or abbreviation. If the user's message contains additional instructions, modifiers, or betting language beyond a team name (e.g., "lay the points", "take the under", "bet $5", "Celtics -6.5"), act on it if the intent is unambiguous — otherwise, ask the user to clarify before proceeding.

## Defaults

| Parameter | Default |
|-----------|---------|
| Market | moneyline (override with spread/total via input like "Lakers -6.5" or "over 220.5") |
| Amount | 3 USDC (maximum per transaction) |
| Side | the named team |
| Odds multiplier | 1.05 |
| Odds | market odds × odds multiplier |

## Communication Rules

Do not send progress updates during Steps 1–3. Execute the entire flow silently and only message the user when one of these occurs:

- **Ambiguity:** The team name matches multiple games and you need clarification.
- **Counter-offer:** Michelle offered worse odds and you need the user's approval before proceeding.
- **Final result:** Step 4 — the position is created and matched (or the match fallback message).
- **Hard failure:** An API error, on-chain revert, or any condition that stops the flow.

If any API call returns an unexpected error, stop and report it. Do not silently retry or work around failures.

## Step 1: Resolve Team and Market Type

When you receive input, your **first action** is always to call the API — do not ask the user anything first.

**Detect market type from input:**
- If the input includes a point value (e.g., "Atlanta -6.5", "Boise +1.5", "Celtics -3") → market type is **spread**, line is the number with its sign
- If the input includes "over" or "under" with a number (e.g., "Lakers over 220.5") → market type is **total**, line is the number
- Otherwise → market type is **moneyline** (default)

**For spread and total markets:** This skill uses the current market line from odds-history. If the user specifies a line that differs from the current market (e.g., user says "+1.5" but market is at -3.5), inform the user of the current line and ask if they want to proceed at that line instead. Do not attempt to create a position at a non-market line.

1. Call `GET /markets`.
2. Search all responses for the input text in `homeTeam` and `awayTeam` fields. Note: All API responses use a `{ data: ..., formatted: "..." }` envelope. The `formatted` field is a display convenience — never use it for branching decisions. Always use the `data` object. When searching for teams, look inside the `data` array, not the top-level response.
3. If found in exactly one game → that is the team and game. Note `contestId`, `matchTime`, and whether the team is home or away. Check the `speculations` array for an existing `speculationId` matching the detected market type. If one exists, note the `speculationId`. For spread speculations, note `awayLine` (away team's perspective, e.g. -3.5) and `homeLine` (home team's perspective, e.g. 3.5) — use whichever matches the user's team. For total speculations, note the `line` field. These are the actual on-chain lines for the bet. If no `speculationId` exists for the detected market type, respond: "No {marketType} market available for {team} right now." and stop.
4. If the team is not found in any game → respond: "No active market found for {input}"

Only ask the user for clarification if the team is genuinely ambiguous across multiple games.

## Step 2: Get a Quote from the agent market maker (Michelle)

First, determine the odds to request. Call `GET /analytics/odds-history/{contestId}` to get current market odds. Select the odds for the detected market type:

- **Moneyline:** Use `current.moneyline.awayOdds` or `current.moneyline.homeOdds` depending on which team the user picked.
- **Spread:** Use `current.spread.awayOdds` or `current.spread.homeOdds`.
- **Total:** Use `current.total.overOdds` or `current.total.underOdds`.

Apply the odds multiplier from the Defaults section and floor to 2 decimal places: `Math.floor(marketOdds * oddsMultiplier * 100) / 100`. This is your minimum acceptable odds.

Note: Higher decimal odds = higher payout for the bettor. A quote at 2.00 is better than 1.85.

If the odds-history endpoint returns no data for the relevant market type, use 1.91 as a reasonable default for spread/total (standard -110 line). For moneyline, ask the user — don't guess, since moneyline odds vary widely depending on the matchup.

**Line reporting:** For spread markets, use `awayLine` or `homeLine` from the speculation (noted in Step 1) depending on the user's team — not the line from odds-history. For total markets, use `line` from the speculation. Odds-history is for odds values only — ignore its `line` field for spread/total direction. The speculation's line fields are the actual on-chain lines the user is betting on. Use these in all user-facing messages including the Step 4 result.

**Line validation:** The quote API requires lines in .5 increments (e.g., -10.5, +3.5, 195.5). If the line from odds-history is a whole number or does not end in .5, stop and tell the user: "Spread/total line unavailable for this market right now." Do not attempt to convert it.

Request a quote from Michelle using the speculationId from Step 1:
```
POST /instant-match/{speculationId}/quote?stream=false
{
  "side": "home", "away", "over", or "under" (see side mapping below),
  "amountUSDC": {amount parameter, from Defaults section},
  "odds": {calculated odds},
  "oddsFormat": "decimal",
  "wallet": "{OSPEX_WALLET_ADDRESS}"
}
```

This returns: `quoteId`, `approved`, `approvedOddsDecimal`, `approvedOddsAmerican`, `expiresAt`. If Michelle counters, the response also includes a `counterOffer` object. The quote expires at `expiresAt`. Steps 3-4 must complete before this time.

**Save the `txParams` object from the response — you will need it in Step 3.** This contains all pre-computed on-chain transaction parameters. Do not compute these values yourself.

- If `approved` is false → tell the user "Michelle (market maker agent) declined — {reason}" and stop.
- If `approvedOddsDecimal` is **greater than or equal to** your requested odds → Michelle is offering the same or better. Keep moving, no confirmation needed.
- If `approvedOddsDecimal` is **less than** your requested odds → Michelle is offering worse odds. **Stop and confirm with the user** before proceeding. Show them the counter-offer, let the user know that they must respond before the counter expires, and ask if they want to accept. If the user accepts, call:
```
POST /instant-match/{quoteId}/accept-counter
Body: { "wallet": "{OSPEX_WALLET_ADDRESS}" }
```
This returns an updated `txParams` object. **Use the txParams from this response** (it reflects the accepted counter-offer terms).

**For `side`:** For moneyline and spread: if the user's team is the `homeTeam` → `"home"`. If `awayTeam`