📝

Web3Auth SDKバージョン違いで送金できないトラブルを防止

に公開

はじめに

Web3Authを使ってPolygon Amoyテストネットでウォレット開発をしていたところ、SDKバージョンの違いで作成したウォレットアドレスが異なるという問題に直面しました。

この記事では、以下の内容を解説します:

  • 🔍 Web3Auth SDKバージョンによるウォレットアドレスの違い
  • 🔑 古いバージョンで作成したウォレットから秘密鍵を取り出す方法
  • 💸 異なるバージョン間での送金テスト方法
  • ⚡ Polygon Amoyでのガス最適化

対象読者

  • Web3Authを使ったウォレット開発をしている方
  • Polygon Amoyテストネットで開発している方
  • SDKバージョン移行で困っている方

問題の発生

状況

古い実装 (Torus SDK v1.21.0)

<script src="https://cdn.jsdelivr.net/npm/@toruslabs/torus-embed@1.21.0/dist/torus.min.js"></script>
<script>
  const torus = new window.Torus();
  await torus.init({
    network: { host: "mumbai" }
  });
  await torus.login();
</script>

生成されたアドレス: 0x588**************************faD8Fd

新しい実装 (Web3Auth v9)

<script src="https://cdn.jsdelivr.net/npm/@web3auth/base@9/dist/base.umd.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@web3auth/ethereum-provider@9/dist/ethereumProvider.umd.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@web3auth/modal@9/dist/modal.umd.min.js"></script>
<script>
  const web3auth = new window.Modal.Web3Auth({
    clientId: "YOUR_CLIENT_ID",
    web3AuthNetwork: "sapphire_devnet",
    chainConfig: {
      chainNamespace: "eip155",
      chainId: "0x13882", // Polygon Amoy
      rpcTarget: "https://rpc-amoy.polygon.technology"
    }
  });
  await web3auth.initModal();
  await web3auth.connect();
</script>

生成されたアドレス: 0x42**********************fc660cb

❌ 問題点

同じGoogleアカウントでログインしても、アドレスが異なる!

旧アドレスには 0.1 POL の残高があるのに、新アドレスでは使えない...


解決方法

方法1: Web3Auth Walletで秘密鍵を取得 (推奨)

Web3Authには公式ウォレットUIがあり、ここから秘密鍵をエクスポートできます。

手順

1. Web3Auth Walletにアクセス

https://wallet.web3auth.io/

2. ログイン

  • 同じSNSアカウント(Google等)でログイン
  • 重要: ログイン時にTestnet/Mainnetトグルを確認
    • Testnetで作成したウォレット → Testnetモードでログイン
    • Mainnetで作成したウォレット → Mainnetモードでログイン

3. 秘密鍵をエクスポート

  1. ログイン後、右上のアカウントアイコンをクリック
  2. AccountSettingsを開く
  3. Private KeyセクションでShow Private Keyをクリック
  4. 確認ダイアログで承認
  5. 秘密鍵(64文字の16進数)が表示される
例: a1b2c3d4e5f6...

4. MetaMaskにインポート

  1. MetaMaskを開く
  2. アカウントメニュー → アカウントをインポート
  3. 秘密鍵を選択
  4. コピーした秘密鍵を貼り付け
  5. インポートをクリック

5. Polygon Amoyネットワークを追加

MetaMaskにPolygon Amoyが未登録の場合:

// 手動追加の設定値
Network Name: Polygon Amoy Testnet
RPC URL: https://rpc-amoy.polygon.technology
Chain ID: 80002
Currency Symbol: POL
Block Explorer: https://amoy.polygonscan.com/

または、以下のリンクで自動追加:

https://chainlist.org/?search=amoy

方法2: 古いSDKで秘密鍵を取得

Torus SDK v1.21.0を使っている場合、コード上で秘密鍵を取得できます。

// Torus SDK v1.21.0
const torus = new window.Torus();
await torus.init({ network: { host: "mumbai" } });
await torus.login();

// 秘密鍵を取得
const privateKey = await torus.getPrivateKey();
console.log("Private Key:", privateKey);

⚠️ セキュリティ注意:

  • 秘密鍵は絶対に他人に見せない
  • GitHubなどに公開しない
  • 本番環境では絶対にconsole.logしない

この方法は、Github Copilotで提案されましたが、実際はエラーが出て取り出せませんでした。
方法1で、成功しました。

同じGoogleなどのSNSのアカウントでテストしていてもSDKのバージョンの違いで、ウォレットのアドレスが異なることがあります。

Polygon Amoyでの送金実装

MATICからPOLへのリブランド

2024年9月、PolygonのネイティブトークンがMATICからPOLにリブランドされました。

項目 旧名称 新名称
トークン名 MATIC POL
コントラクト 同一 同一
互換性 - 完全互換

コード上は同じですが、表記はPOLに統一しましょう。

EIP-1559対応のガス計算

Polygon AmoyはEIP-1559に対応しているため、適切なガス価格設定が必要です。

ガス料金取得関数

async function getFees(provider) {
  // ベースフィーをノードから取得
  const gasPriceHex = await provider.request({ 
    method: "eth_gasPrice" 
  });
  
  // 優先手数料(Tip)のデフォルト: 1 gwei
  let tipHex = "0x3b9aca00"; // 1 gwei
  
  try {
    const r = await provider.request({ 
      method: "eth_maxPriorityFeePerGas" 
    });
    if (typeof r === "string") tipHex = r;
  } catch (_) {}

  const base = BigInt(gasPriceHex);
  const tip = BigInt(tipHex);

  // maxFeePerGas = ベースフィー × 1.1 (安全マージン)
  const maxFee = (base * 11n) / 10n;

  return {
    maxFeePerGas: "0x" + maxFee.toString(16),
    maxPriorityFeePerGas: "0x" + tip.toString(16),
  };
}

POL送金の実装

async function sendPOL(provider, userAddress, toAddress, amount) {
  // 送金額をweiに変換
  const valueInWei = Math.round(amount * 1e18);
  const value = "0x" + valueInWei.toString(16);
  
  console.log(`💸 送金額: ${amount} POL`);
  console.log(`💸 送金額 (wei): ${valueInWei}`);
  console.log(`💸 送金額 (hex): ${value}`);
  
  // ガス料金を取得
  const fee = await getFees(provider);
  console.log(`✅ ガス料金取得完了:`, fee);
  
  // ガスリミット: POL送金は21,000 × 2 = 42,000 (安全マージン)
  const gas = "0xA410"; // 42,000 gas
  
  // トランザクション構築
  const tx = { 
    from: userAddress, 
    to: toAddress, 
    value, 
    gas, 
    ...fee 
  };
  
  console.log("⏳ POL送金中...", JSON.stringify(tx));
  
  // 送信
  const hash = await provider.request({ 
    method: "eth_sendTransaction", 
    params: [tx] 
  });
  
  console.log(`✅ POL送金完了: ${hash}`);
  console.log(`🌐 https://amoy.polygonscan.com/tx/${hash}`);
  
  return hash;
}

ガスリミットの決定

Polygon Amoyではeth_estimateGasが不安定なため、固定値を推奨します:

トランザクション種類 標準ガス 推奨設定 理由
POL送金 21,000 42,000 (×2) 標準的な転送
ERC20送金 65,000 97,500 (×1.5) コントラクト実行
コントラクト操作 変動 見積もり×2 複雑な処理
// POL送金
const gas = "0xA410"; // 42,000 (21,000 × 2)

// ERC20トークン送金
const gas = "0x17CDC"; // 97,500 (65,000 × 1.5)

エラーと対処法

よくあるエラー

1. gas required exceeds allowance

原因: ガスリミットが不足している

解決策:

// ❌ 不足する例
const gas = "0x5208"; // 21,000のみ

// ✅ 十分な例
const gas = "0xA410"; // 42,000 (×2)

2. insufficient funds for gas * price + value

原因: 残高不足

解決策:

// 必要残高の計算
const required = sendAmount + (gasLimit × maxFeePerGas);
console.log(`必要残高: ${required} wei`);

3. nonce too low

原因: 同じnonceで複数回送信した

解決策:

// nonceを明示的に取得
const nonce = await provider.request({
  method: "eth_getTransactionCount",
  params: [userAddress, "latest"]
});

const tx = { from, to, value, gas, ...fee, nonce };

テストネット情報

Polygon Amoy

項目 設定値
Network Name Polygon Amoy Testnet
RPC URL https://rpc-amoy.polygon.technology
Chain ID 80002 (0x13882)
Currency POL
Explorer https://amoy.polygonscan.com/

Faucet情報

公式Faucet

https://faucet.polygon.technology/
  • 取得量: 0.2 POL
  • 制限: 24時間に1回
  • 認証: Twitter/GitHub

代替Faucet

  1. Chainlink Faucet

  2. Alchemy Faucet


完全な実装例

HTML + JavaScript実装

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Web3Auth POL送金デモ</title>
</head>
<body>
  <h1>Web3Auth + Polygon Amoy</h1>
  
  <button id="btnInit">初期化</button>
  <button id="btnLogin">ログイン</button>
  <button id="btnSend">0.001 POL送金</button>
  
  <div id="info"></div>
  
  <!-- Web3Auth v9 -->
  <script src="https://cdn.jsdelivr.net/npm/@web3auth/base@9/dist/base.umd.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/@web3auth/ethereum-provider@9/dist/ethereumProvider.umd.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/@web3auth/modal@9/dist/modal.umd.min.js"></script>
  
  <script>
    let web3auth;
    let provider;
    let userAddress;
    
    const CLIENT_ID = "YOUR_WEB3AUTH_CLIENT_ID"; // 要設定
    
    // 初期化
    document.getElementById('btnInit').onclick = async () => {
      try {
        const chainConfig = {
          chainNamespace: "eip155",
          chainId: "0x13882", // Polygon Amoy
          rpcTarget: "https://rpc-amoy.polygon.technology",
          displayName: "Polygon Amoy Testnet",
          blockExplorer: "https://amoy.polygonscan.com/",
          ticker: "POL",
          tickerName: "POL"
        };
        
        const privateKeyProvider = new window.EthereumProvider.EthereumPrivateKeyProvider({
          config: { chainConfig }
        });
        
        web3auth = new window.Modal.Web3Auth({
          clientId: CLIENT_ID,
          web3AuthNetwork: "sapphire_devnet",
          privateKeyProvider
        });
        
        await web3auth.initModal();
        console.log("✅ 初期化完了");
      } catch (e) {
        console.error("❌ 初期化エラー:", e);
      }
    };
    
    // ログイン
    document.getElementById('btnLogin').onclick = async () => {
      try {
        provider = await web3auth.connect();
        
        const accounts = await provider.request({ method: "eth_accounts" });
        userAddress = accounts[0];
        
        const balanceHex = await provider.request({
          method: "eth_getBalance",
          params: [userAddress, "latest"]
        });
        const balance = parseInt(balanceHex, 16) / 1e18;
        
        document.getElementById('info').innerHTML = `
          <p>アドレス: ${userAddress}</p>
          <p>残高: ${balance.toFixed(8)} POL</p>
        `;
        
        console.log("✅ ログイン完了");
      } catch (e) {
        console.error("❌ ログインエラー:", e);
      }
    };
    
    // ガス料金取得
    async function getFees(provider) {
      const gasPriceHex = await provider.request({ method: "eth_gasPrice" });
      let tipHex = "0x3b9aca00"; // 1 gwei
      
      try {
        const r = await provider.request({ method: "eth_maxPriorityFeePerGas" });
        if (typeof r === "string") tipHex = r;
      } catch (_) {}
      
      const base = BigInt(gasPriceHex);
      const tip = BigInt(tipHex);
      const maxFee = (base * 11n) / 10n;
      
      return {
        maxFeePerGas: "0x" + maxFee.toString(16),
        maxPriorityFeePerGas: "0x" + tip.toString(16),
      };
    }
    
    // 送金
    document.getElementById('btnSend').onclick = async () => {
      try {
        const to = prompt("送金先アドレス:");
        if (!to) return;
        
        const amount = 0.001;
        const value = "0x" + Math.round(amount * 1e18).toString(16);
        const fee = await getFees(provider);
        const gas = "0xA410"; // 42,000
        
        const tx = { from: userAddress, to, value, gas, ...fee };
        console.log("⏳ 送金中...", tx);
        
        const hash = await provider.request({
          method: "eth_sendTransaction",
          params: [tx]
        });
        
        console.log(`✅ 送金完了: ${hash}`);
        alert(`送金完了!\nhttps://amoy.polygonscan.com/tx/${hash}`);
      } catch (e) {
        console.error("❌ 送金エラー:", e);
        alert("送金失敗: " + e.message);
      }
    };
  </script>
</body>
</html>

まとめ

チェックリスト

  • Web3Auth Client IDを取得
  • 正しいSDKバージョンを使用(v9推奨)
  • Polygon Amoyネットワーク設定
  • EIP-1559対応のガス計算実装
  • 固定ガスリミット設定(POL: 42,000, ERC20: 97,500)
  • エラーハンドリング実装
  • テストネットトークン取得(Faucet)

ベストプラクティス

✅ DO (推奨)

  • 最新のSDKを使用 (Web3Auth v9)
  • 固定ガスリミットで安定動作
  • EIP-1559対応のガス計算
  • 詳細ログでデバッグしやすく
  • 秘密鍵は安全に管理

❌ DON'T (非推奨)

  • 古いTorus SDKの継続使用
  • eth_estimateGasに全面依存
  • ガスリミットを21,000固定
  • 秘密鍵をコンソールに出力
  • 本番環境でTestnet設定

参考リンク

公式ドキュメント

ツール

GitHub


おわりに

Web3AuthのSDKバージョン違いによるウォレットアドレスの違いは、開発中によく遭遇する問題です。

この記事が、同じ問題で困っている方の助けになれば幸いです!


Discussion