mcpField
L
lp-integration

lp-integration

github.com/uniswap開発236446 回インストール2 日前に更新
リポジトリ

lp-integration について

LP Integrationは、Uniswap LP APIを通じてフロントエンド、バックエンド、ボットに流動性提供を組み込むためのスキルです。V2、V3、V4の承認確認とトランザクション構築を扱い、署名とブロードキャストはアプリケーション側で行います。

主な機能

  1. 01トークン承認とpermitデータの確認
  2. 02V2、V3、V4の流動性ポジション作成
  3. 03既存流動性の増減
  4. 04V3、V4の取引手数料の請求
  5. 05プール状態の読み取りと未署名トランザクションの構築

活用シーン

  1. 01アプリケーションへの流動性提供機能の追加
  2. 02V3またはV4の集中流動性ポジション作成
  3. 03流動性操作前の承認確認
  4. 04流動性ボット向けトランザクション構築

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/uniswap/uniswap-ai --skill lp-integration

インストール後、Claude Code で/lp-integrationを入力して読み込まれたか確認します。

同梱ファイルreferences/advanced-patterns.md

詳細

LP 統合

Uniswap LP API を使用して、Uniswap の流動性提供をフロントエンド、バックエンド、ボットに統合します。

LP API はトランザクション構築サービスです。ポジションのパラメーターを送信すると、API は最新のプール状態を取得し、依存するトークン量を計算して、完成した署名なしトランザクションを返します。お使いのアプリケーションが署名してブロードキャストします。API が鍵を保持したり、資金を移動したり、ブロードキャストしたりすることはありません。

前提条件

このスキルでは、viem の基本事項(クライアントのセットアップ、アカウント管理、コントラクトとのやり取り、トランザクション署名)を理解していることを前提としています。viem/wagmi の包括的なガイダンスを利用するには、uniswap-viem プラグインをインストールしてください:claude plugin add @uniswap/uniswap-viem

トークンスワップ(流動性提供ではありません)については、このプラグインに含まれる関連スキル swap-integration を参照してください。

ベース URL

LP_API_BASE_URL = https://liquidity.api.uniswap.org

すべての LP エンドポイントは、/lp/ プレフィックスの下にある POST リクエストです(例:https://liquidity.api.uniswap.org/lp/create)。

LP API のホストは、スワップ用の Trading API とは意図的に異なります。 流動性提供には https://liquidity.api.uniswap.org(/v1 プレフィックスなし)を使用します。スワップで使用する https://trade-api.gateway.uniswap.org/v1 ホストではありません。ベース URL は(以下のように)単一の定数として保持し、今後変更があった場合に一か所の編集で済むようにしてください。

認証

すべての書き込み/承認エンドポイントでは、x-api-key ヘッダーで API キーを送信する必要があります。キーがない場合や無効な場合は、{"code":"unauthenticated"} を伴う 401 が返されます。(/lp/pool_info は読み取りエンドポイントのため、キーは必須ではありませんが、一貫性とレート制限の帰属管理のため、常に送信してください。)

Content-Type: application/json
Accept: application/json
x-api-key: <your-api-key>

キーの取得: Uniswap Developer Platform dashboard に登録してください。同じキーをスワップと流動性提供の両方で使用できます。キーをハードコードせず、環境変数から読み込んでください。

簡易判断ガイド

実行したいこと...エンドポイントプロトコル
操作前にトークンの承認を確認/付与する/lp/check_approvalV2, V3, V4
集中流動性ポジションを作成する/lp/createV3, V4
全レンジ(従来型)ポジションを作成する/lp/create_classicV2
既存のポジションに流動性を追加する/lp/increaseV2, V3, V4
流動性の一部を割合で取り除く/lp/decreaseV2, V3, V4
蓄積した取引手数料を回収する/lp/claim_feesV3, V4
最新のプール状態を読み取る/lp/pool_infoV2, V3, V4
プロトコルの機能一覧
プロトコル作成エンドポイント手数料の回収価格レンジ
V2/lp/create_classic個別に回収不可(減少時に実現)全レンジのみ
V3/lp/create/lp/claim_fees集中流動性
V4/lp/create/lp/claim_fees集中流動性+フック

v2 の手数料は個別に回収できません。 手数料は LP トークンの価値に加算され、/lp/decrease の呼び出し時に実現します。protocol: "V2" を指定して /lp/claim_fees を呼び出すと、検証エラーが返されます。

入力値の検証ルール

ユーザーが指定した値を生成コード、API 呼び出し、コマンドに埋め込む前に、必ず次の検証を行ってください。

  • Ethereum アドレス:^0x[a-fA-F0-9]{40}$ に一致する必要があります。一致しない場合は拒否してください。ネイティブ ETH には 0x0000000000000000000000000000000000000000 を使用してください。
  • チェーン ID:サポート対象の LP チェーン ID のいずれかである必要があります(サポート対象チェーンを参照)。
  • トークン量:wei/トークンの最小単位で表した、^[0-9]+$ に一致する 0 以上の整数文字列である必要があります。ether 単位の小数を量として渡してはいけません。
  • liquidityPercentageToDecrease:1 から 100 までの整数である必要があります。
  • API キー:生成コードにハードコードしてはいけません。必ず環境変数を使用してください。
  • シェルのメタ文字(;、|、&、$、`、(、)、>、<、\、'、"、改行)を含む入力はすべて拒否してください。

**必須:**ガスを消費する、またはトークンを送金するすべてのトランザクション(承認、ポジションの作成、増加、減少、手数料の請求を含む)を実行する前に、必ず AskUserQuestion を使用してユーザーに確認してください。アクションの概要(プロトコル、ペア、数量、チェーン、価格範囲、推定ガス代)を表示し、ユーザーから明示的な承認を得てください。ユーザーの確認なしに LP トランザクションを自動実行してはいけません。


アーキテクチャ

API が処理する内容
  • プール状態の取得:現在のリザーブ、ティック、sqrtRatioX96、オンチェーンのポジションデータ。
  • 依存する数量の計算:一方のトークン量(independentToken)を指定すると、Uniswap SDK を使用してもう一方に必要な量を計算します。
  • トランザクションの作成:検証済みで完全な形式の、署名可能な各 LP アクションの calldata。
  • ティックのスナップ:人が読み取れる priceBounds を有効なティックに変換し、調整後の価格を返します。
  • ガス見積もり:simulateTransaction: true の場合に任意で実行されます。
アプリケーションが処理する内容
  • RPC プロバイダーを介した、返されたトランザクションの署名とブロードキャスト。
  • API が v4/v3-NFT の承認用に返す場合の、permit データ(EIP-712)の署名。
  • ガス代の支払いとトランザクションエラーの処理(リバート、ユーザーへのエラー表示)。
データフロー
User intent (create / increase / decrease / claim)
   |
Your application
   |-- POST /lp/check_approval        -> returns approval transactions and/or permit data
   |-- (execute approval txns; sign permit if returned)
   |-- POST /lp/create | increase | decrease | claim_fees
   |-- validate the returned transaction payload (non-empty data, valid addresses)
   |-- user signature (wallet)
   +-- broadcast (your RPC)
        |
   Blockchain

エンドポイントリファレンス

以下のすべてのフィールド名は、LP API の OpenAPI コントラクトに由来します。公開されている統合ガイドで異なる名前が使われている場合は、ここに記載されたコントラクト上の名前が優先されます。

POST /lp/check_approval

必ず最初に呼び出します。LP アクションの前に必要な承認トランザクション(および/または permit データ)を返します。レスポンスの transactions 配列が空で、permit データも返されない場合、承認はすべて完了しています。ただし、kycRequiredWarnings が空でない場合は例外です。これは、プールに許可制トークンが含まれており、ウォレットが許可リストに登録されていないことを意味します。この場合もレスポンスは 200 です。LP アクションを試みる代わりに、各警告の kycUrl から KYC のコールトゥアクションを表示してください。

リクエスト

{
  "walletAddress": "0x...",
  "protocol": "V4",
  "chainId": 1,
  "lpTokens": [
    { "tokenAddress": "0x...", "amount": "1000000000000000000" },
    { "tokenAddress": "0x...", "amount": "500000000" }
  ],
  "action": "CREATE"
}
フィールド必須備考
walletAddressはいポジションの所有者
protocolはいV2 | V3 | V4
chainIdはいサポート対象のチェーン ID
lpTokensはい使用する { tokenAddress, amount } の配列
actionはいCREATE | INCREASE | DECREASE | MIGRATE
simulateTransactionいいえガス見積もりを含めます
includeGasInfoいいえ返される承認にガス情報を含めます
generatePermitAsTransactionいいえtrue の場合、型付きデータの代わりに実行可能なトランザクションとして permit を返します
urgencyいいえNORMAL | FAST | URGENT
v3NftTokenIdいいえポジションマネージャー NFT を承認する際の v3 NFT ID

レスポンス

interface CheckApprovalResponse {
  requestId: string;
  transactions: ApprovalTransactionRequest[]; // sign each .transaction; empty (with kycRequiredWarnings also empty) = nothing to approve
  v4BatchPermitData?: NullablePermit; // v4: sign and pass into /lp/create or /lp/increase
  v3NftPermitData?: NullablePermit; // v3 NFT permit
  kycRequiredWarnings: KycRequiredWarning[]; // permissioned pools: non-empty when the wallet is NOT allowlisted. Always present (usually []).
}

interface ApprovalTransactionRequest {
  transaction: TransactionRequest; // the tx to sign and broadcast
  cancelApproval: boolean;
  action: 'CREATE' | 'INCREASE' | 'DECREASE' | 'MIGRATE';
  gasFee?: string;
}

interface KycRequiredWarning {
  kycUrl: string;
  tokenAddress: string;
  chainId: number;
}

レスポンスフィールドは approvals ではなく transactions です。 各要素はトランザクションをラップしています。要素オブジェクト自体ではなく、element.transaction に署名してください。トップレベルに token/spender フィールドはありません。

POST /lp/create

v3 または v4 の集中流動性ポジションを作成します。価格範囲と一方のトークン量を指定すると、API がプールの最新状態からもう一方の量を計算します。

プールの指定 — 次のいずれか一方を指定します。

  • existingPool:{ token0Address, token1Address, poolReference }。poolReference はプールアドレス(v3)またはプール ID(v4)です。
  • newPool:{ token0Address, token1Address, fee, tickSpacing, hooks?, initialPrice }。initialPrice は sqrtRatioX96 文字列です(hooks は v4 専用です)。

価格範囲 — 次のいずれか一方を指定します。

  • priceBounds:{ minPrice, maxPrice, quotedTokenAddress } — minPrice/maxPrice は小数形式の価格文字列で、quotedTokenAddress は必須です(token0Address または token1Address と一致する必要があります)。quotedTokenAddress は価格の建値となるトークンを指定します。デフォルト値はなく、省略すると 400 が返されます。API は有効なティックに合わせて価格を調整し、調整後の価格を返します。
  • tickBounds:{ tickLower, tickUpper } の整数値。

リクエスト

{
  "walletAddress": "0x...",
  "chainId": 1,
  "protocol": "V3",
  "existingPool": {
    "token0Address": "0x1f9840a85d5af5bf1d1762f925bdaddc4201f984",
    "token1Address": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
    "poolReference": "0x3470447f3cecffac709d3e783a307790b0208d60"
  },
  "independentToken": {
    "tokenAddress": "0x1f9840a85d5af5bf1d1762f925bdaddc4201f984",
    "amount": "198251669183062942"
  },
  "priceBounds": {
    "minPrice": "0.00000000000324",
    "maxPrice": "0.00000000000393",
    "quotedTokenAddress": "0xdAC17F958D2ee523a2206206994597C13D831ec7"
  },
  "simulateTransaction": false
}
フィールド必須備考
walletAddress、chainId、protocol、independentTokenはいindependentToken は { tokenAddress, amount } です
existingPool | newPoolいずれか必須上記のプールの指定を参照してください
priceBounds | tickBoundsいずれか必須上記の価格範囲を参照してください
dependentTokenいいえ計算された従属トークン量を上書きします
slippageToleranceいいえ小数のパーセント(例:0.5)
deadlineいいえUnix 秒
simulateTransactionいいえレスポンスに gasFee を含めます
urgencyいいえNORMAL | FAST | URGENT
batchPermitData + signatureいいえ/lp/check_approval の v4 許可データ(注:create では batchPermitData を使用します)
nativeTokenBalanceいいえ片方のトークンがネイティブ ETH の場合に使用します
includeApprovalSimulationいいえガスシミュレーションに承認の事前呼び出しを含めます(simulateTransaction: true の場合のみ)

レスポンス

interface CreatePositionResponse {
  requestId: string;
  token0: LPToken; // { tokenAddress, amount }
  token1: LPToken;
  adjustedMinPrice: string; // show THIS to the user, not your input minPrice
  adjustedMaxPrice: string;
  tickLower: number;
  tickUpper: number;
  create: TransactionRequest;
  gasFee?: string; // present when simulateTransaction: true
  slippage?: number; // effective slippage the API applied (e.g. native-token v4 cases)
}

元のリクエストの priceBounds ではなく、(ティックに合わせて調整された値である)adjustedMinPrice/adjustedMaxPrice を表示してください。

POST /lp/create_classic

v2(全範囲)のポジションを作成します。independentToken の数量を指定すると、API が現在のペアのリザーブから dependentToken の数量を計算します。

リクエスト

{
  "walletAddress": "0x...",
  "poolParameters": {
    "token0Address": "0xc02fe7317d4eb8753a02c35fe019786854a92001",
    "token1Address": "0x0000000000000000000000000000000000000000",
    "chainId": 130
  },
  "independentToken": {
    "tokenAddress": "0xc02fe7317d4eb8753a02c35fe019786854a92001",
    "amount": "1000000000000000"
  },
  "simulateTransaction": false
}
フィールド必須備考
walletAddressはい
poolParametersはい{ token0Address, token1Address, chainId }
independentTokenはい{ tokenAddress, amount }
dependentTokenいいえ省略すると、リザーブから計算されます。新規プールを作成する場合は必須です。
slippageTolerance、deadline、simulateTransaction、urgency、includeApprovalSimulationいいえ

レスポンス:{ requestId, independentToken, dependentToken, create: TransactionRequest, gasFee? }。create_classic のレスポンスでは、トークン名に token0 / token1 ではなく independentToken / dependentToken が使用される点に注意してください。

POST /lp/increase

既存の v2/v3/v4 ポジションに流動性を追加します。一方のトークンの数量を指定すると、API がもう一方の数量を計算します。

リクエスト

{
  "walletAddress": "0x...",
  "chainId": 130,
  "protocol": "V4",
  "token0Address": "0x0000000000000000000000000000000000000000",
  "token1Address": "0x078D782b760474a361dDA0AF3839290b0EF57AD6",
  "nftTokenId": "1833079",
  "independentToken": {
    "tokenAddress": "0x078D782b760474a361dDA0AF3839290b0EF57AD6",
    "amount": "8223"
  },
  "simulateTransaction": false
}
フィールド必須備考
walletAddress、chainId、protocol、token0Address、token1Address、independentTokenはいtoken0Address / token1Address は既存のポジションの順序と一致している必要があります
nftTokenIdv3/v4ポジションを識別する NFT ID
slippageTolerance、deadline、simulateTransaction、urgencyいいえ
v4BatchPermitData + signatureいいえ/lp/check_approval の v4 permit(注意:increase では v4BatchPermitData を使用し、create では batchPermitData を使用します)
nativeTokenBalance、includeApprovalSimulation、permissionedいいえnativeTokenBalance は片方のトークンがネイティブ ETH の場合に指定します。permissioned を指定すると、許可制の PositionManager にルーティングされます(KYC が必要なプール)

レスポンス:{ requestId, token0, token1, increase: TransactionRequest, gasFee?, slippage? }。

ネイティブ ETH:0x0000000000000000000000000000000000000000 を使用してください。API は余剰分を返すための refundETH を含むマルチコールを生成します。

POST /lp/decrease

v2/v3/v4のポジションから、指定した割合の流動性を削除します。

リクエスト

{
  "walletAddress": "0x...",
  "chainId": 130,
  "protocol": "V4",
  "token0Address": "0x0000000000000000000000000000000000000000",
  "token1Address": "0x078D782b760474a361dDA0AF3839290b0EF57AD6",
  "liquidityPercentageToDecrease": 25,
  "nftTokenId": "1833079",
  "simulateTransaction": false
}
フィールド必須備考
walletAddress, chainId, protocol, token0Address, token1Address, liquidityPercentageToDecreaseはい割合は1~100の整数です
nftTokenIdv3/v4ポジションを識別するNFT ID
withdrawAsWethいいえV2とV3に適用されます(V4では無視されます)。デフォルト/trueの場合はWETHのままにし、falseの場合はWETHをネイティブETHにラップ解除します。
permissionedいいえ許可制のPositionManager(KYCが必要なプール)にルーティングします。
slippageTolerance, deadline, simulateTransaction, urgencyいいえ

レスポンス:{ requestId, token0, token1, decrease: TransactionRequest, gasFee? }。

v3の流動性削除時の手数料回収:v3では、返されるdecreaseのcalldataに未回収の手数料が自動的に出金分として含まれるため、別途/lp/claim_feesを呼び出す必要はありません。レスポンスのtoken0/token1の数量には、比例配分で削除された流動性分のみが反映されます。回収される手数料はcalldataに含まれ、レスポンスの数量には加算されません。

POST /lp/claim_fees

v3またはv4のポジションから累積した取引手数料を回収します。v2では利用できません。

リクエスト

{
  "protocol": "V4",
  "walletAddress": "0x...",
  "chainId": 130,
  "tokenId": "1833079",
  "simulateTransaction": false
}
フィールド必須備考
protocol, walletAddress, chainId, tokenIdはいprotocolがV2の場合、バリデーションエラーが返されます。注意:回収ではnftTokenIdではなくtokenIdを使用します。
collectAsWethいいえv3のみ。falseの場合、WETHをネイティブETHにラップ解除します。
permissionedいいえ許可制のPositionManager(KYCが必要なプール)にルーティングします。
simulateTransactionいいえ

レスポンス:{ requestId, token0, token1, claim: TransactionRequest, gasFee? }。

POST /lp/pool_info

1 つ以上のプールの現在の状態(リザーブ、tick、sqrtRatioX96、流動性)を読み取ります。

リクエスト: { protocol, poolParameters?, poolReferences?, chainId?, pageSize?, currentPage? }(必須なのは protocol のみです。プールを指定するには、poolParameters { tokenAddressA, tokenAddressB, fee?, tickSpacing?, hookAddress? } または poolReferences を指定します)。

レスポンス: { requestId, pools: PoolInformation[], pageSize, currentPage }。各 PoolInformation には、poolReferenceIdentifier, poolProtocol, tokenAddressA, tokenAddressB, tickSpacing, fee, hookAddress, chainId, tokenAmountA, tokenAmountB, tokenDecimalsA, tokenDecimalsB, poolLiquidity, sqrtRatioX96, currentTick, token0Reserves, token1Reserves が含まれます(注:アドレス/数量/小数桁数のフィールドには A/B の接尾辞が付きますが、2 つのリザーブフィールドには 0/1 が付きます)。該当するプールに適用されない場合、任意フィールド(例:数量、hookAddress、リザーブ)は省略されます。

保持すべきフィールド名の違い

同じ概念でも、エンドポイントによって名前が異なります。エンドポイントごとに正確な名前を使用してください。

概念/lp/claim_fees/lp/increase、/lp/decrease/lp/check_approval
ポジション NFT IDtokenIdnftTokenIdv3NftTokenId (整数)
概念/lp/create/lp/increase
v4 permit ペイロードbatchPermitDatav4BatchPermitData

承認と Permit のフロー

承認が以前に付与されている場合でも、LP 操作を行う前に必ず /lp/check_approval を呼び出してください(使用許可は取り消されたり、消費されたりすることがあります)。

オンチェーン承認
const res = await fetch(`${LP_API_BASE_URL}/lp/check_approval`, {
  method: 'POST',
  headers: { 'x-api-key': API_KEY, 'Content-Type': 'application/json', Accept: 'application/json' },
  body: JSON.stringify({ walletAddress, protocol: 'V4', chainId: 1, lpTokens, action: 'CREATE' }),
});
if (!res.ok) throw new Error(`check_approval failed: ${res.status}`);
const { transactions, v4BatchPermitData, kycRequiredWarnings } = await res.json();

// A permissioned pool gates LPing on KYC: a non-empty kycRequiredWarnings means render the
// KYC CTA (warning.kycUrl) and stop — do NOT treat empty transactions as "approved" here.
if (kycRequiredWarnings?.length)
  throw new Error('Wallet not allowlisted for this permissioned pool');

// transactions is an array of ApprovalTransactionRequest. Empty (and no KYC warnings) => already approved.
for (const approval of transactions) {
  validateLpTransaction(approval.transaction); // see Critical Notes
  const hash = await walletClient.sendTransaction(approval.transaction);
  await publicClient.waitForTransactionReceipt({ hash });
}
v4 permit(EIP-712 署名して返却)

v4 の場合、check_approval は v4BatchPermitData(ガス不要の Permit2 一括承認)を返します。オンチェーンの ERC-20 → Permit2 承認用 transactions とともに返されることも多く、これらの代わりになるわけではありません。返された transactions を先に実行してください(Permit2 には ERC-20 の使用許可が必要です)。その後、オフチェーンで permit に署名し、次の呼び出しに渡します。

**permit ペイロードは proto 形式でエンコードされており、そのまま viem では使用できません。**署名する前に 2 つのフィールドを正規化してください。正規化前のオブジェクトを API に返します。

  • domain.chainId は数値ではなく、チェーンの列挙名文字列(例:"UNICHAIN")として届きます。数値のチェーン ID に置き換えてください。
  • 各 types エントリは { fields: [...] } としてラップされています。viem(および EIP-712 仕様)では配列のままの形式が必要なため、.fields を取り出してください。
let signature: string | undefined;
if (v4BatchPermitData) {
  // Normalize the proto-encoded permit into viem's TypedData shape (for signing only).
  const types = Object.fromEntries(
    Object.entries(v4BatchPermitData.types).map(([k, v]) => [k, (v as any).fields])
  );
  const domain = { ...v4BatchPermitData.domain, chainId }; // numeric chainId (e.g. 130), NOT "UNICHAIN"

  signature = await walletClient.signTypedData({
    domain,
    types,
    message: v4BatchPermitData.values,
    primaryType: 'PermitBatch',
  });
}

// Send the ORIGINAL (un-normalized) permit back to the API, with the signature.
// /lp/create (uses batchPermitData) ...
const createBody = { ...createParams, batchPermitData: v4BatchPermitData, signature };
// ... or /lp/increase (uses v4BatchPermitData)
const increaseBody = { ...increaseParams, v4BatchPermitData, signature };

v3 NFT permit、EIP712Domain のエッジケース、移行に関する完全な型付き実装については、高度なパターンのリファレンスを参照してください。

重要な実装上の注意事項

1. ラッパーではなく、ラップされたトランザクションに署名する

/lp/check_approval は transactions: ApprovalTransactionRequest[] を返します。各要素は { transaction, cancelApproval, action, gasFee? } です。

// WRONG — signs the wrapper object, not a transaction
for (const a of transactions) await walletClient.sendTransaction(a);

// CORRECT
for (const a of transactions) await walletClient.sendTransaction(a.transaction);
2. コントラクトのレスポンスフィールド名を使用する
// WRONG — these names come from the narrative guide, not the contract
const { approvals } = await checkApprovalRes.json();
const { minPrice, maxPrice } = createResponse;

// CORRECT
const { transactions } = await checkApprovalRes.json();
const { adjustedMinPrice, adjustedMaxPrice } = createResponse;
3. data フィールドは変更せず、必ず検証する

create/increase/decrease/claim フィールドには、事前に検証済みの calldata が格納されています。

function validateLpTransaction(tx: TransactionRequest): void {
  if (!tx.data || tx.data === '' || tx.data === '0x') throw new Error('Empty transaction data');
  if (!tx.to || !isAddress(tx.to)) throw new Error('Invalid recipient address');
  if (!tx.from || !isAddress(tx.from)) throw new Error('Invalid sender address');
  if (tx.maxFeePerGas && tx.gasPrice) throw new Error('Cannot set both maxFeePerGas and gasPrice');
}

calldata は編集しないでください。変更すると、トランザクションがリバートしたり、資金が失われたりする可能性があります。

4. トランザクションには有効期限があります

プール価格は変動します。ユーザーが確認に約 30 秒以上かかる場合は、ブロードキャストする前にトランザクションを再取得してください。

const TX_EXPIRY_MS = 30_000;
const builtAt = Date.now();
// ... user reviews ...
if (Date.now() - builtAt > TX_EXPIRY_MS) lpTx = await refetchLpTransaction(params);
5. 数量は wei 文字列です

すべての amount フィールドは、トークンの最小単位で表した整数文字列です。parseUnits(value, decimals).toString() で変換してください。ether 単位の小数を送信しないでください。

6. 未定義の任意フィールドを除去する

permit フィールドはペア(batchPermitData + signature)で送信するか、両方とも省略してください。permit があるのに signature: undefined を送信したり、その逆を行ったりしないでください。

実例:v3 ポジションを作成する(viem)

import { createWalletClient, createPublicClient, http, isAddress, parseUnits } from 'viem';
import { mainnet } from 'viem/chains';
import { privateKeyToAccount } from 'viem/accounts';

const LP_API_BASE_URL = 'https://liquidity.api.uniswap.org';
const API_KEY = process.env.UNISWAP_API_KEY!; // never hardcode

const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);
const walletClient = createWalletClient({
  account,
  chain: mainnet,
  transport: http(process.env.RPC_URL),
});
const publicClient = createPublicClient({ chain: mainnet, transport: http(process.env.RPC_URL) });

const headers = {
  'x-api-key': API_KEY,
  'Content-Type': 'application/json',
  Accept: 'application/json',
};

async function lpFetch(path: string, body: object) {
  const res = await fetch(`${LP_API_BASE_URL}${path}`, {
    method: 'POST',
    headers,
    body: JSON.stringify(body),
  });
  if (!res.ok) throw new Error(`${path} failed: ${res.status} ${await res.text()}`);
  return res.json();
}

// Same checks as the canonical validateLpTransaction in "Critical Implementation Notes" above.
function validateLpTransaction(tx: any) {
  if (!tx || !tx.data || tx.data === '' || tx.data === '0x')
    throw new Error('Empty transaction data');
  if (!tx.to || !isAddress(tx.to)) throw new Error('Invalid recipient address');
  if (!tx.from || !isAddress(tx.from)) throw new Error('Invalid sender address');
  if (tx.maxFeePerGas && tx.gasPrice) throw new Error('Cannot set both maxFeePerGas and gasPrice');
}

async function createV3Position() {
  const protocol = 'V3';
  const chainId = 1;
  const token0Address = '0x1f9840a85d5af5bf1d1762f925bdaddc4201f984'; // UNI
  const token1Address = '0xdAC17F958D2ee523a2206206994597C13D831ec7'; // USDT
  const independentToken = { tokenAddress: token0Address, amount: parseUnits('10', 18).toString() };

  // 1. Approvals
  const { transactions } = await lpFetch('/lp/check_approval', {
    walletAddress: account.address,
    protocol,
    chainId,
    lpTokens: [independentToken],
    action: 'CREATE',
  });
  for (const a of transactions) {
    validateLpTransaction(a.transaction);
    const hash = await walletClient.sendTransaction(a.transaction);
    await publicClient.waitForTransactionReceipt({ hash });
  }

  // 2. Build the create transaction
  const created = await lpFetch('/lp/create', {
    walletAddress: account.address,
    chainId,
    protocol,
    independentToken,
    existingPool: {
      token0Address,
      token1Address,
      poolReference: '0x3470447f3cecffac709d3e783a307790b0208d60',
    },
    priceBounds: {
      minPrice: '0.00000000000324',
      maxPrice: '0.00000000000393',
      quotedTokenAddress: token1Address,
    },
    simulateTransaction: true,
  });

  // 3. CONFIRM WITH USER before broadcasting (use AskUserQuestion in the skill flow):
  //    pair, amounts (created.token0 / created.token1), adjusted range
  //    (created.adjustedMinPrice / created.adjustedMaxPrice), created.gasFee
  validateLpTransaction(created.create);
  const hash = await walletClient.sendTransaction(created.create);
  return publicClient.waitForTransactionReceipt({ hash });
}

コンパニオン SDK

API は署名準備済みのトランザクションを返すため、プロトコル SDK は不要です。クライアント側でのプール/ポジション計算、ティック変換、検証には、開発者は一般に LP API と次のものを組み合わせて使用します。

  • @uniswap/sdk-core — チェーン、Token、CurrencyAmount、Percent、Price(基礎機能)。
  • @uniswap/v3-sdk — v3 のティック計算(TickMath、nearestUsableTick)、Pool、Position。
  • @uniswap/v4-sdk — v4 のプールキー、フックを考慮した Position のモデリング。
  • @uniswap/v2-sdk — v2 のペア/流動性計算。
  • viem(または ethers) — 返された { to, data, value } に署名してブロードキャストし、EIP-712 の許可データに署名します。

エラー処理

コード意味対応
400検証エラーリクエストフィールドを修正します(下記のよくあるエラーを参照)
401無効な API キーx-api-key ヘッダーを確認します
429レート制限指数バックオフを行い、繰り返しの読み取りをキャッシュします
500API エラーバックオフして再試行します
503一時的な利用不可再試行します

エラーボディ(Connect プロトコル):{ code: string, message: string, details?: Array<{ type: string; value: string }> } — 例:{"code":"invalid_argument","message":"RequestValidationError: ...","details":[...]}。code は Connect のエラーコード文字列(invalid_argument、unauthenticated、failed_precondition、internal、…)であり、HTTP の数値ではありません。また、トップレベルに error フィールドはありません。body.error ではなく、body.code/body.message を読み取ってください。

よくあるエラー

  • v2 の手数料請求を試みた場合:protocol: "V2" を指定して /lp/claim_fees を呼び出しています。v2 の手数料を実現するには /lp/decrease を使用してください。
  • プールが見つからない場合:トークンアドレス、poolReference、チェーンを確認してください。または newPool を使用して初期化してください。
  • 流動性不足:計算された従属金額が残高を超えています。independentToken.amount を減らすか、レンジを広げてください。
  • 検証エラー:選択した protocol に必要なフィールドがすべて指定されていること、アドレスがチェックサム形式であること、金額の単位が wei であること、liquidityPercentageToDecrease が 1~100 であることを確認してください。

再試行/バックオフ、リクエストのキャッシュ、モニタリング、ブロードキャスト前の完全なチェックリストについては、高度なパターンのリファレンスを参照してください。

対応チェーン

LP API は固定されたチェーン ID のセットに対応しています。送信前に、chainId がこのセットに含まれていることを確認してください。

1, 10, 56, 130, 137, 143, 196, 324, 480, 1868, 4217, 4326, 4663, 5042,
8453, 10143, 42161, 42220, 43114, 59144, 81457, 7777777, 1301, 84532, 11155111

(Mainnet 1、Optimism 10、BNB 56、Unichain 130、Polygon 137、Base 8453、Arbitrum 42161、Avalanche 43114、Linea 59144、Blast 81457、Zora 7777777、Sepolia 11155111 など)。最新の対応状況は、/lp/pool_info の利用可否または対応チェーンのドキュメントで確認してください。

追加リソース

コメント