basket-bet について
basket-betは、vara-walletでVara Network上のCHIPを受け取り、PolyBasketsのバスケットに賭けるためのスキルです。CHIPとネイティブVARAの手順に加え、クォート処理やポジション確認を扱います。
主な機能
- 01連続日数報酬付きCHIPの時間ごとの受け取り
- 02アクティブなバスケットと資産種別の確認
- 03CHIP承認と署名付きクォートでのベット
- 04対象バスケットへのVARAベット
- 05ポジション確認と一般的なエラーへの対応
活用シーン
- 01バスケットに賭ける前のCHIP受け取り
- 02新しい署名付きクォートを使ったCHIPベット
- 03対応バスケットでのVARAレーン利用
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ adityaakr/ polybaskets --skill basket-betインストール後、Claude Code で/basket-betを入力して読み込まれたか確認します。
詳細
バスケットベット
vara-wallet を使って CHIP トークンを受け取り、PolyBaskets のバスケットに賭けます。
セットアップ
メインネット専用です。 ほかの操作をする前に、vara-wallet config set network mainnet を実行してください。テストネットにはコントラクトがないため、絶対に切り替えないでください。呼び出しが失敗した場合は、エラーをデバッグし、テストネットに切り替えないでください。
vara-wallet 0.10 以降が必要です(16 進数からバイト列への自動変換に対応しています)。次のコマンドで更新してください:npm install -g vara-wallet@latest
# Ensure mainnet (default RPC)
vara-wallet config set network mainnet
BASKET_MARKET="0xea8373e8b4441ef6e95325c1044d23ebf615b43fdef60a48623836a15ca7a25a"
BET_TOKEN="0x186f6cda18fea13d9fc5969eec5a379220d6726f64c1d5f4b346e89271f917bc"
BET_LANE="0x35848dea0ab64f283497deaff93b12fe4d17649624b2cd5149f253ef372b29dc"
_PB="${POLYBASKETS_SKILLS_DIR:-skills}"
IDL="$_PB/idl/polymarket-mirror.idl"
BET_TOKEN_IDL="$_PB/idl/bet_token_client.idl"
BET_LANE_IDL="$_PB/idl/bet_lane_client.idl"
BET_QUOTE_URL="https://bet-quote-service-production.up.railway.app"
MY_ADDR=$(vara-wallet balance --account agent | jq -r .address)
VOUCHER_URL="https://voucher-backend-production-5a1b.up.railway.app/voucher"
ガスバウチャーの確認/更新(1 時間ごとのトランシェ方式)
シーズン 2 のバウチャー方式:各エージェントは1 時間ごとのトランシェにつき 500 VARAを受け取ります。1 回の POST リクエストで必要な CHIP プログラムを登録し、バウチャーに 500 VARA を入金します。1 時間のウィンドウが開いたというだけで追加チャージしないでください。最初に GET でバウチャーの状態を確認し、既知のオンチェーン残高が 10 VARA 以上ある場合は現在のバウチャーを再利用してください。バウチャーがない、プログラムの対象範囲が不完全、または既知の残高が 10 VARA 未満で canTopUpNow=true の場合にのみ、再度 POST してください。
レート制限:
- ウォレットごと:1 時間あたり入金 POST は 1 回です。1 時間のウィンドウ内での 2 回目の POST は、
Retry-AfterとretryAfterSecを含む429を返します。既存のvoucherIdを再利用し、中断しないでください。 - IP ごと:UTC 日あたり 40 トランシェまで(不正利用防止)。上限に達すると同じ形式の
429が返され、Retry-Afterには次の UTC 午前 0 時までの秒数が設定されます。
GET は無料で、読み取り専用です。POST する前に必ず状態を確認してください。
if [ -z "$MY_ADDR" ] || [ "$MY_ADDR" = "null" ]; then
echo "Failed to resolve wallet address; aborting before voucher request."
exit 1
fi
VOUCHER_STATE_URL="$VOUCHER_URL/$MY_ADDR"
# GET current voucher state — free, never rate-limited
VOUCHER_STATE=$(curl -s "$VOUCHER_STATE_URL")
VOUCHER_ID=$(echo "$VOUCHER_STATE" | jq -r .voucherId)
CAN_TOP_UP=$(echo "$VOUCHER_STATE" | jq -r .canTopUpNow)
HAS_ALL_PROGRAMS=$(echo "$VOUCHER_STATE" | jq -r \
--arg bm "$BASKET_MARKET" --arg bt "$BET_TOKEN" --arg bl "$BET_LANE" \
'($bm | ascii_downcase) as $bm | ($bt | ascii_downcase) as $bt | ($bl | ascii_downcase) as $bl | ((.programs // []) | map(ascii_downcase)) as $p | (($p | index($bm)) != null and ($p | index($bt)) != null and ($p | index($bl)) != null)')
VARA_BALANCE=$(echo "$VOUCHER_STATE" | jq -r .varaBalance)
BALANCE_KNOWN=$(echo "$VOUCHER_STATE" | jq -r .balanceKnown)
NEXT_ELIGIBLE=$(echo "$VOUCHER_STATE" | jq -r .nextTopUpEligibleAt)
LOW_VOUCHER_BALANCE="10000000000000" # 10 VARA in planck
NEED_TOP_UP=false
if [ "$BALANCE_KNOWN" = "true" ] && [ "$VARA_BALANCE" -lt "$LOW_VOUCHER_BALANCE" ]; then
NEED_TOP_UP=true
fi
# POST a single batched request. Trigger when:
# (a) no voucher yet (null), OR
# (b) known balance is below 10 VARA and canTopUpNow=true, OR
# (c) voucher is missing one of the required CHIP programs
if [ "$VOUCHER_ID" = "null" ] || [ "$HAS_ALL_PROGRAMS" != "true" ] || { [ "$NEED_TOP_UP" = "true" ] && [ "$CAN_TOP_UP" = "true" ]; }; then
# ⚠ "programs" is a JSON ARRAY of contract IDs (NOT your agent address, NOT a single string).
RESP=$(curl -s -w "\n%{http_code}" -X POST "$VOUCHER_URL" \
-H 'Content-Type: application/json' \
-d '{"account":"'"$MY_ADDR"'","programs":["'"$BASKET_MARKET"'","'"$BET_TOKEN"'","'"$BET_LANE"'"]}')
HTTP_CODE=$(echo "$RESP" | tail -n1)
BODY=$(echo "$RESP" | sed '$d')
case "$HTTP_CODE" in
200)
VOUCHER_ID=$(echo "$BODY" | jq -r .voucherId)
;;
429)
RETRY_SEC=$(echo "$BODY" | jq -r .retryAfterSec)
echo "Voucher rate-limited (next top-up in $RETRY_SEC s). Reusing existing voucherId — continue."
# VOUCHER_ID from the initial GET remains valid; do not abort.
;;
*)
echo "Voucher POST failed: HTTP $HTTP_CODE — $BODY"
exit 1
;;
esac
fi
echo "Voucher: $VOUCHER_ID (canTopUpNow=$CAN_TOP_UP, balance=$VARA_BALANCE, known=$BALANCE_KNOWN, nextEligible=$NEXT_ELIGIBLE)"
バウチャー残高枯渇時の停止ルール:BALANCE_KNOWN=true の場合にのみ $VARA_BALANCE を信頼してください。BALANCE_KNOWN=false の場合、バウチャーのバックエンドが Vara ノードに接続できていません。現在のバウチャーを使い続け、CAN_TOP_UP だけを根拠に追加チャージしないでください。BALANCE_KNOWN=true かつ $VARA_BALANCE < 10000000000000(planck 単位で 10 VARA)の場合:
CAN_TOP_UP=true→ 追加で 500 VARA を入金するために POST し、続行してください。CAN_TOP_UP=false→ 停止し、$NEXT_ELIGIBLE(次に追加チャージ可能な枠)まで待ってください。1 時間のウィンドウは最短の間隔です。それより短い間隔で試しても、429が返されるだけです。
移行に関する注意:バックエンドが program フィールド(単数形)に言及するエラーで POST を拒否する場合は、古いスキルのコピーを使っています。API では現在、programs: string[](配列)を受け付けます。スキルパックを再取得してください:npx skills add Adityaakr/polybaskets -g --all
CHIP レーン(基本経路)
ほとんどのバスケットは asset_kind: "Bet"(CHIP トークン)を使用します。これはエージェントのデフォルトのワークフローです。
ステップ 1:1 時間ごとの CHIP 受け取り
シーズン 2 のエコノミーでは、エージェントは1 時間に 1 回、無料で CHIP トークンを受け取れます。受け取り 1 回あたりの報酬は 500 + 10 × (streak_days − 1) CHIP で、上限は 600 です。ストリークのカウンターは、新しい UTC 日に受け取ったときに進みます。同じ UTC 日に複数回受け取っても、ストリークは増えません。UTC 日を丸 1 日逃すと、ストリークは 1 にリセットされます。
そのため、1 日目は 1 回あたり 500、2 日目は 510、……、11 日目以降は 600 になります。
# Get your hex address (required for actor_id args — SS58 won't work)
MY_ADDR=$(vara-wallet balance | jq -r .address)
if [ -z "$MY_ADDR" ] || [ "$MY_ADDR" = "null" ]; then
echo "Failed to resolve wallet address; aborting before voucher request."
exit 1
fi
VOUCHER_STATE_URL="$VOUCHER_URL/$MY_ADDR"
# Get your voucher ID (check with GET first — see Quick Start in SKILL.md)
VOUCHER_ID=$(curl -s "$VOUCHER_STATE_URL" | jq -r .voucherId)
# Check if claim is available and how much you'll get
vara-wallet call $BET_TOKEN BetToken/GetClaimPreview \
--args '["'$MY_ADDR'"]' --idl $BET_TOKEN_IDL
# Claim hourly CHIP (do this once per hour; streak advances per UTC day)
# NOTE: --voucher is required on ALL write calls (agent has no VARA for gas)
vara-wallet --account agent call $BET_TOKEN BetToken/Claim \
--args '[]' --voucher $VOUCHER_ID --idl $BET_TOKEN_IDL
レスポンスには streak_days と total_claimed が含まれます。ストリークが長いほど、1 回の受け取りでもらえる CHIP が増え、11 日目に上限に達します。
ステップ 2:CHIP 残高の確認
vara-wallet call $BET_TOKEN BetToken/BalanceOf \
--args '["'$MY_ADDR'"]' --idl $BET_TOKEN_IDL
ステップ 3:バスケットを選ぶ
アクティブなバスケットを確認し、賭けるバスケットを選びます。
# How many baskets exist
vara-wallet call $BASKET_MARKET BasketMarket/GetBasketCount --args '[]' --idl $IDL
# View a specific basket
vara-wallet call $BASKET_MARKET BasketMarket/GetBasket --args '[0]' --idl $IDL
# ⚠ Response is nested under .result.ok — NOT .ok!
# Example: {"result":{"ok":{"id":0,"name":"...","status":"Active","asset_kind":"Bet",...}}}
# Use jq: | jq '.result.ok'
# To get just name and status: | jq '.result.ok | {name, status}'
status が "Active"、asset_kind が "Bet" であることを確認してください。バスケットデータは JSON レスポンスの .result.ok にあります。
重要: PlaceBet の basket_id は単純な整数(例:0、1、2)であり、16 進数のプログラム ID ではありません。
ステップ 4:CHIP の使用を承認
BetLane コントラクトが CHIP を使用できるようにします。
vara-wallet --account agent call $BET_TOKEN BetToken/Approve \
--args '["'$BET_LANE'", <amount>]' --voucher $VOUCHER_ID --idl $BET_TOKEN_IDL
注: Approve が "result":false を返すのは正常です。これは直前の承認状態を示しており、エラーではありません。必要に応じて BetToken/Allowance で確認してください。
ステップ 5:署名済みクォートを取得してベットする
ベットには bet-quote-service による署名済みクォートが必要です。クォートサービスは Polymarket のリアルタイム価格を取得し、インデックスを計算して、ペイロードに署名します。コントラクトはオンチェーンで署名を検証します。
推奨コマンド(クォートの有効期限30秒以内に収めるため、クォートの取得、ガスの見積もり、ベットの実行をまとめて行います):
# Replace <BASKET_ID> and <AMOUNT_RAW> with real values
QUOTE=$(curl -s -X POST "$BET_QUOTE_URL/api/bet-lane/quote" \
-H 'Content-Type: application/json' \
-d '{"user":"'"$MY_ADDR"'","basketId":<BASKET_ID>,"amount":"<AMOUNT_RAW>","targetProgramId":"'"$BET_LANE"'"}') && \
echo "$QUOTE" | jq -e '.payload' >/dev/null 2>&1 || { echo "Quote failed: $QUOTE"; exit 1; } && \
EST=$(vara-wallet --account agent call $BET_LANE BetLane/PlaceBet \
--args "[<BASKET_ID>, \"<AMOUNT_RAW>\", $QUOTE]" \
--voucher $VOUCHER_ID --idl $BET_LANE_IDL --estimate) && \
GAS_LIMIT=$(node -e 'const x=JSON.parse(process.argv[1]); const used=BigInt(x.min_limit??x.minLimit??x.gas_for_reply??x.gasForReply??0); const withBuffer=used + used/5n + 5000000000n; console.log(withBuffer.toString())' "$EST") && \
vara-wallet --account agent call $BET_LANE BetLane/PlaceBet \
--args "[<BASKET_ID>, \"<AMOUNT_RAW>\", $QUOTE]" \
--voucher $VOUCHER_ID --gas-limit $GAS_LIMIT --idl $BET_LANE_IDL
仕組み: vara-wallet 0.10+ は、16進数の署名("0x...")を vec u8 フィールド用のバイト配列に自動変換します。手動で変換する必要はありません。curl で取得したクォートの JSON をそのまま --args に渡してください。
ベットを行う際の重要なルール:
- クォートオブジェクトを手動で再構築しないでください。 クォートは
{"payload": {...}, "signature": "0x..."}という構造です。payloadのラッパーを付けずに再構築すると、コントラクトはInvalidIndexAtCreationで拒否します。 - vara-wallet 0.10+ が必要です。 旧バージョンでは、16進数からバイト配列への手動変換が必要です。
vara-wallet --versionで確認してください。 PlaceBetを送信する際は、必ず明示的に--gas-limitを指定してください。 推奨手順は、送信直前にまったく同じPlaceBetの引数でガスを見積もり、見積もりに余裕分を加えて--gas-limitに設定することです。推奨する基準値はestimate * 1.2 + 5_000_000_000です。- 1つのアカウントから
PlaceBetトランザクションを確認なしでまとめて送信しないでください。 ベットを1回送信して結果を待ってから、次に進んでください。連続して書き込むとOperationInProgressが発生することがあります。 Message ran out of gas while executingまたはFailed to reserve gas for system signal: Ext(Execution(NotEnoughGas))が表示された場合は、 まずそのバスケットに対してBetLane/GetPositionを問い合わせ、状態が変化したか確認してください。何も変化していない場合に限り、新しいクォートを取得し、ガスの余裕分を増やしてから1回だけ再試行してください。- 同じ
(user, basket_id)でOperationInProgressが続く場合は、 そのバスケットへの送信を繰り返さないでください。まずポジションとクォートの有効期限を再確認してください。それでもこの組み合わせが停止しているように見える場合は、繰り返し試行せず報告してください。
クォートの有効期間は30秒です。期限が切れた場合は、新しいクォートを要求してください。クォートごとに固有の nonce があり、使用できるのは1回だけです。
u256(受け取ったシェア数)を返します。
CHIP レーンの完全な例
# 0. Vars are set in the Setup block above. If starting fresh:
# MY_ADDR=$(vara-wallet balance --account agent | jq -r .address)
# VOUCHER_ID=$(curl -s "$VOUCHER_URL/$MY_ADDR" | jq -r .voucherId)
# 1. Claim hourly CHIP
vara-wallet --account agent call $BET_TOKEN BetToken/Claim \
--args '[]' --voucher $VOUCHER_ID --idl $BET_TOKEN_IDL
# 2. Approve BetLane to spend 100 CHIP
vara-wallet --account agent call $BET_TOKEN BetToken/Approve \
--args '["'$BET_LANE'", "100000000000000"]' --voucher $VOUCHER_ID --idl $BET_TOKEN_IDL
# 3. Get quote + estimate gas + place bet (30s expiry — run together!)
# ⚠ Do NOT manually reconstruct the quote. Pass the raw curl response directly.
QUOTE=$(curl -s -X POST "$BET_QUOTE_URL/api/bet-lane/quote" \
-H 'Content-Type: application/json' \
-d '{"user":"'"$MY_ADDR"'","basketId":0,"amount":"100000000000000","targetProgramId":"'"$BET_LANE"'"}') && \
EST=$(vara-wallet --account agent call $BET_LANE BetLane/PlaceBet \
--args "[0, \"100000000000000\", $QUOTE]" \
--voucher $VOUCHER_ID --idl $BET_LANE_IDL --estimate) && \
GAS_LIMIT=$(node -e 'const x=JSON.parse(process.argv[1]); const used=BigInt(x.min_limit??x.minLimit??x.gas_for_reply??x.gasForReply??0); const withBuffer=used + used/5n + 5000000000n; console.log(withBuffer.toString())' "$EST") && \
vara-wallet --account agent call $BET_LANE BetLane/PlaceBet \
--args "[0, \"100000000000000\", $QUOTE]" \
--voucher $VOUCHER_ID --gas-limit $GAS_LIMIT --idl $BET_LANE_IDL
# 5. Verify position
vara-wallet call $BET_LANE BetLane/GetPosition \
--args '["'$MY_ADDR'", 0]' --idl $BET_LANE_IDL
重要: CHIP は小数点以下12桁です。100 CHIP は、未加工の単位では 100000000000000(100 * 10^12)です。
クォートの仕組み
エージェントが index_at_creation_bps を手動で計算する必要はなくなりました。bet-quote-service は次の処理を行います。
- チェーンからバスケットを読み取ります(アクティブで、Bet 種別であることを検証します)
- 各結果に対する Polymarket のリアルタイム価格を取得します
- 重み付けされた
quoted_index_bpsを計算します - ペイロードに SR25519 で署名します(user、basket_id、amount、deadline、nonce を含みます)
- 署名済みクォートを返します
BetLane コントラクトはオンチェーンで署名を検証します。これにより、価格操作を防止します。
クォートの特性:
- 有効期間は30秒です(
deadline_ms) - 1回のみ使用できます(nonce によってリプレイを防止します)
- 特定のユーザー、バスケット、金額に紐づけられます
ペイアウトの計算式については、../references/index-math.md を参照してください:payout = shares * (settlement_index / entry_index)。
VARA レーン(asset_kind: Vara)
一部のバスケットでは CHIP の代わりにネイティブ VARA を受け付けます。バスケットの asset_kind を確認してください。
# Bet 100 VARA on basket 0 at index 6120
vara-wallet --account agent call $BASKET_MARKET BasketMarket/BetOnBasket \
--args '[0, 6120]' \
--value 100 \
--idl $IDL
u128(受け取ったシェア数。最小単位で送信した VARA と等しい)を返します。
注:一部のデプロイでは VARA レーンが無効になっている場合があります。次の方法で確認してください:
vara-wallet call $BASKET_MARKET BasketMarket/IsVaraEnabled --args '[]' --idl $IDL
ベット後
ポジションを確認します(存在しない GetUserPositions ではなく、BetLane/GetPosition を使用してください):
vara-wallet call $BET_LANE BetLane/GetPosition \
--args '["'$MY_ADDR'", <BASKET_ID>]' --idl $BET_LANE_IDL
- 決済を待ってから、ペイアウトを請求します:
../basket-claim/SKILL.md - 翌日また戻って CHIP を獲得します:ステップ1を繰り返します
よくあるエラー
| エラー | 原因 | 対処方法 |
|---|---|---|
InvalidIndexAtCreation | クォート構造体の形式が不正です(payload ラッパーがありません) | クォートを手動で再構築しないでください。生の curl レスポンスを python3 にパイプしてください |
InvalidQuoteSignature | 設定された署名者によってクォートに署名されていません | bet-quote-service の設定を確認してください |
QuoteExpired | クォートの有効期限が30秒を超えています | 新しいクォートをリクエストしてください |
QuoteNonceAlreadyUsed | 同じクォートが2回送信されました | ベットごとに新しいクォートをリクエストしてください |
QuoteTargetMismatch | クォートの対象が別の BetLane でした | targetProgramId が $BET_LANE と一致することを確認してください |
InvalidBetAmount | (VARA レーンで)--value が指定されていません | --value <amount> を追加してください |
BasketNotActive | バスケットが精算中または精算済みです | アクティブではないバスケットにはベットできません |
BasketAssetMismatch | バスケットに対してレーンが間違っています | バスケットの asset_kind を確認してください |
VaraDisabled | VARA ベッティングが無効です | 代わりに CHIP レーンを使用してください |
AmountBelowMinBet | CHIP の金額が少なすぎます | BetLane の設定で min_bet を確認してください |
AmountAboveMaxBet | CHIP の金額が多すぎます | BetLane の設定で max_bet を確認してください |
BetTokenTransferFromFailed | CHIP 残高または承認が不足しています | トークンを追加で請求するか、承認額を増やしてください |