Skip to main content
贖回(Redeeming)是市場結算後,將獲勝代幣轉換為 USDC 的過程。這是條件代幣生命周期的最後一步。

什麼是贖回?

當市場結算後,Oracle 會報告最終結果。持有獲勝結果代幣的用戶可以將這些代幣兌換為 USDC:
如果 Yes 獲勝:
100 Yes 代幣 → 100 USDC
100 No 代幣 → 0 USDC (無價值)

如果 No 獲勝:
100 Yes 代幣 → 0 USDC (無價值)
100 No 代幣 → 100 USDC
獲勝回報:每個獲勝代幣可以 1:1 兌換為 USDC。如果你以 0.65 的價格買入 Yes 代幣,Yes 獲勝後,每個代幣價值 1 USDC,利潤為 0.35 USDC。

贖回流程

市場結算流程

  1. 事件發生:現實世界的事件結果確定
  2. Oracle 報告:UMA Oracle 提交結果
  3. 爭議期:短暫的爭議期(通常幾小時)
  4. 結算確認:結果最終確定
  5. 贖回開放:用戶可以贖回獲勝代幣

方法 1:通過 Polymarket 界面

最簡單的方法:
  1. 訪問已結算的市場頁面
  2. 如果你持有獲勝代幣,會看到”Redeem”按鈕
  3. 點擊贖回
  4. 確認交易
  5. USDC 會自動轉入你的錢包
Polymarket 會自動顯示可贖回的金額,並處理所有技術細節。

方法 2:直接調用智能合約

適合自動化和批量贖回。

Python 示例

from web3 import Web3

# 連接到 Polygon
w3 = Web3(Web3.HTTPProvider('https://polygon-rpc.com'))

# CTF 合約
ctf_address = '0x4D97DCd97eC945f40cF65F87097ACe5EA0476045'
ctf_abi = [...]  # CTF ABI
ctf_contract = w3.eth.contract(address=ctf_address, abi=ctf_abi)

# USDC 地址
usdc_address = '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174'

# 市場信息
condition_id = '0x...'  # 已結算市場的條件 ID
parent_collection_id = '0x' + '0' * 64

# 確定獲勝結果的索引集
# 如果 Yes 獲勝,使用 [1]
# 如果 No 獲勝,使用 [2]
winning_index_sets = [1]  # Yes 獲勝

# 贖回獲勝代幣
redeem_tx = ctf_contract.functions.redeemPositions(
    usdc_address,           # 抵押品代幣 (USDC)
    parent_collection_id,   # 父集合 ID
    condition_id,           # 條件 ID
    winning_index_sets      # 獲勝結果的索引集
).build_transaction({
    'from': your_address,
    'nonce': w3.eth.get_transaction_count(your_address),
    'gas': 150000,
    'gasPrice': w3.eth.gas_price
})

# 籤署並發送交易
signed_tx = w3.eth.account.sign_transaction(redeem_tx, private_key)
tx_hash = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
receipt = w3.eth.wait_for_transaction_receipt(tx_hash)

print(f"贖回成功! 交易哈希: {tx_hash.hex()}")
print(f"Gas 使用: {receipt['gasUsed']}")

TypeScript 示例

import { ethers } from 'ethers';

// 連接到 Polygon
const provider = new ethers.providers.JsonRpcProvider('https://polygon-rpc.com');
const wallet = new ethers.Wallet(privateKey, provider);

// CTF 合約
const ctfAddress = '0x4D97DCd97eC945f40cF65F87097ACe5EA0476045';
const ctfAbi = [...];  // CTF ABI
const ctfContract = new ethers.Contract(ctfAddress, ctfAbi, wallet);

// USDC 地址
const usdcAddress = '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174';

async function redeemPositions() {
  const conditionId = '0x...';  // 已結算市場的條件 ID
  const parentCollectionId = '0x' + '0'.repeat(64);
  
  // 獲勝結果 (1 = Yes, 2 = No)
  const winningIndexSets = [1];  // Yes 獲勝
  
  try {
    // 贖回代幣
    const redeemTx = await ctfContract.redeemPositions(
      usdcAddress,
      parentCollectionId,
      conditionId,
      winningIndexSets
    );
    
    console.log('贖回交易已發送:', redeemTx.hash);
    
    const receipt = await redeemTx.wait();
    console.log('贖回成功!');
    console.log('Gas 使用:', receipt.gasUsed.toString());
    
    // 檢查收到的 USDC
    const usdcContract = new ethers.Contract(usdcAddress, usdcAbi, wallet);
    const balance = await usdcContract.balanceOf(wallet.address);
    console.log('USDC 餘額:', ethers.utils.formatUnits(balance, 6));
    
  } catch (error) {
    console.error('贖回失敗:', error);
  }
}

redeemPositions();

檢查市場結算狀態

在贖回前,確認市場已結算:
def check_market_settled(condition_id):
    """
    檢查市場是否已結算
    """
    # 查詢支付向量
    payout_numerators = ctf_contract.functions.payoutNumerators(
        condition_id,
        0  # Yes 的索引
    ).call()
    
    if payout_numerators == 0:
        # 未結算
        print("市場尚未結算")
        return False, None
    else:
        # 已結算,確定獲勝方
        yes_payout = ctf_contract.functions.payoutNumerators(
            condition_id, 0
        ).call()
        
        no_payout = ctf_contract.functions.payoutNumerators(
            condition_id, 1
        ).call()
        
        if yes_payout > no_payout:
            print("市場已結算: Yes 獲勝")
            return True, 'YES'
        elif no_payout > yes_payout:
            print("市場已結算: No 獲勝")
            return True, 'NO'
        else:
            print("市場已結算: 無效/平局")
            return True, 'INVALID'

# 使用示例
settled, winner = check_market_settled(condition_id)

計算可贖回金額

檢查你可以贖回多少 USDC:
def calculate_redeemable_amount(address, position_id, condition_id):
    """
    計算可贖回的 USDC 數量
    """
    # 檢查市場結算狀態
    settled, winner = check_market_settled(condition_id)
    
    if not settled:
        print("市場尚未結算,無法贖回")
        return 0
    
    # 查詢代幣餘額
    balance = ctf_contract.functions.balanceOf(
        address,
        int(position_id, 16)
    ).call()
    
    # 查詢該位置的支付比例
    # 獲勝代幣的支付比例是 1,失敗代幣是 0
    payout = ctf_contract.functions.payoutNumerators(
        condition_id,
        0 if winner == 'YES' else 1
    ).call()
    
    payout_denominator = ctf_contract.functions.payoutDenominator(
        condition_id
    ).call()
    
    # 計算可贖回金額
    redeemable = balance * payout / payout_denominator
    
    print(f"代幣餘額: {balance / 10**6}")
    print(f"支付比例: {payout}/{payout_denominator}")
    print(f"可贖回: {redeemable / 10**6} USDC")
    
    return redeemable

# 使用示例
yes_position_id = '0x...'
redeemable = calculate_redeemable_amount(
    your_address,
    yes_position_id,
    condition_id
)

批量贖回多個市場

如果你在多個已結算市場都有獲勝代幣:
def batch_redeem(markets_info):
    """
    批量贖回多個市場的代幣
    
    Args:
        markets_info: 列表,每個元素包含 {condition_id, index_sets}
    """
    total_redeemed = 0
    
    for market in markets_info:
        condition_id = market['condition_id']
        index_sets = market['index_sets']  # [1] for Yes, [2] for No
        
        try:
            # 檢查是否已結算
            settled, winner = check_market_settled(condition_id)
            if not settled:
                print(f"市場 {condition_id[:8]}... 尚未結算,跳過")
                continue
            
            # 贖回
            redeem_tx = ctf_contract.functions.redeemPositions(
                usdc_address,
                '0x' + '0' * 64,
                condition_id,
                index_sets
            ).build_transaction({
                'from': your_address,
                'nonce': w3.eth.get_transaction_count(your_address),
            })
            
            signed_tx = w3.eth.account.sign_transaction(redeem_tx, private_key)
            tx_hash = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
            
            print(f"市場 {condition_id[:8]}... 贖回交易: {tx_hash.hex()}")
            
            receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
            print(f"  ✓ 已確認,Gas: {receipt['gasUsed']}")
            
        except Exception as e:
            print(f"市場 {condition_id[:8]}... 贖回失敗: {e}")
            continue

# 使用示例
markets = [
    {'condition_id': '0xabc...', 'index_sets': [1]},  # Yes 獲勝
    {'condition_id': '0xdef...', 'index_sets': [2]},  # No 獲勝
    {'condition_id': '0x123...', 'index_sets': [1]},  # Yes 獲勝
]

batch_redeem(markets)

自動監控和贖回

創建一個自動化腳本,監控市場結算並自動贖回:
import time
from datetime import datetime

def auto_redeem_monitor(condition_ids, check_interval=300):
    """
    監控市場結算並自動贖回
    
    Args:
        condition_ids: 要監控的條件 ID 列表
        check_interval: 檢查間隔(秒)
    """
    redeemed = set()  # 已贖回的市場
    
    while condition_ids:
        print(f"\n[{datetime.now()}] 檢查市場狀態...")
        
        for condition_id in condition_ids[:]:
            if condition_id in redeemed:
                continue
            
            try:
                # 檢查是否已結算
                settled, winner = check_market_settled(condition_id)
                
                if settled:
                    print(f"市場 {condition_id[:8]}... 已結算: {winner}")
                    
                    # 確定要贖回的索引
                    if winner == 'YES':
                        index_sets = [1]
                    elif winner == 'NO':
                        index_sets = [2]
                    else:
                        # 無效結果,可能需要贖回兩邊
                        index_sets = [1, 2]
                    
                    # 執行贖回
                    redeem_tx = ctf_contract.functions.redeemPositions(
                        usdc_address,
                        '0x' + '0' * 64,
                        condition_id,
                        index_sets
                    ).build_transaction({
                        'from': your_address,
                        'nonce': w3.eth.get_transaction_count(your_address),
                    })
                    
                    signed_tx = w3.eth.account.sign_transaction(
                        redeem_tx, private_key
                    )
                    tx_hash = w3.eth.send_raw_transaction(
                        signed_tx.rawTransaction
                    )
                    
                    print(f"  → 贖回交易: {tx_hash.hex()}")
                    receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
                    print(f"  ✓ 贖回成功")
                    
                    # 標記為已贖回
                    redeemed.add(condition_id)
                    condition_ids.remove(condition_id)
                    
                else:
                    print(f"市場 {condition_id[:8]}... 尚未結算")
                    
            except Exception as e:
                print(f"市場 {condition_id[:8]}... 錯誤: {e}")
        
        if condition_ids:
            print(f"\n等待 {check_interval} 秒後再次檢查...")
            time.sleep(check_interval)
        else:
            print("\n所有市場已贖回完成!")
            break

# 使用示例
markets_to_monitor = [
    '0xabc123...',
    '0xdef456...',
    '0x789ghi...',
]

auto_redeem_monitor(markets_to_monitor, check_interval=300)  # 每5分鐘檢查

Gas 成本

贖回操作的典型 Gas 成本(Polygon):
操作Gas 使用量成本 @ 100 Gwei
單次贖回~110,000$0.011
批量贖回 (3個)~300,000$0.030

特殊情況

無效結果

某些情況下,市場可能被標記為無效:
  • 問題模糊不清
  • 結果無法驗證
  • Oracle 無法確定結果
在無效結果的情況下:
# 無效結果通常意味著 Yes 和 No 各 50%
# 你可能需要贖回兩邊
index_sets = [1, 2]  # 贖回 Yes 和 No

# 每個代幣可以贖回 0.5 USDC
# 如果持有 100 Yes,可以贖回 50 USDC

部分贖回

你可以選擇只贖回部分代幣(雖然不常見):
# 這需要更複雜的合約交互
# 通常直接贖回全部更簡單

贖回後驗證

驗證贖回是否成功:
def verify_redemption(address, condition_id):
    """
    驗證贖回後的狀態
    """
    # 檢查 USDC 餘額
    usdc_balance = usdc_contract.functions.balanceOf(address).call()
    print(f"USDC 餘額: {usdc_balance / 10**6}")
    
    # 檢查代幣餘額(應該為 0 或減少)
    yes_balance = ctf_contract.functions.balanceOf(
        address,
        int(yes_position_id, 16)
    ).call()
    
    no_balance = ctf_contract.functions.balanceOf(
        address,
        int(no_position_id, 16)
    ).call()
    
    print(f"Yes 代幣餘額: {yes_balance / 10**6}")
    print(f"No 代幣餘額: {no_balance / 10**6}")
    
    if yes_balance == 0 and no_balance == 0:
        print("✓ 所有代幣已贖回")
    else:
        print("⚠ 仍有代幣未贖回")

verify_redemption(your_address, condition_id)

注意事項

重要提示
  1. 等待結算:市場必須完全結算後才能贖回
  2. 爭議期:UMA Oracle 有爭議期,通常需要等待數小時
  3. 失敗代幣:失敗方的代幣贖回價值為 0
  4. Gas 費用:需要 MATIC 支付 Gas
  5. 時間限制:理論上沒有時間限制,但建議儘早贖回
  6. 檢查結果:確認獲勝方後再贖回,避免 Gas 浪費

常見問題

Q: 贖回有截止日期嗎?

A: 沒有。你可以隨時贖回獲勝代幣,即使是幾個月後。

Q: 如果忘記贖回會怎樣?

A: 代幣會一直在你的錢包裡,不會消失。你可以隨時回來贖回。

Q: 可以部分贖回嗎?

A: 技術上可以,但通常一次性贖回全部更簡單。

Q: 贖回失敗的代幣有價值嗎?

A: 沒有。失敗方的代幣在結算後變為無價值,無法贖回任何 USDC。

Q: Polymarket 會自動贖回嗎?

A: 不會。你需要手動點擊贖回按鈕或調用智能合約。

下一步