lp-integration について
LP Integrationは、Uniswap LP APIを通じてフロントエンド、バックエンド、ボットに流動性提供を組み込むためのスキルです。V2、V3、V4の承認確認とトランザクション構築を扱い、署名とブロードキャストはアプリケーション側で行います。
主な機能
- 01トークン承認とpermitデータの確認
- 02V2、V3、V4の流動性ポジション作成
- 03既存流動性の増減
- 04V3、V4の取引手数料の請求
- 05プール状態の読み取りと未署名トランザクションの構築
活用シーン
- 01アプリケーションへの流動性提供機能の追加
- 02V3またはV4の集中流動性ポジション作成
- 03流動性操作前の承認確認
- 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_approval | V2, V3, V4 |
| 集中流動性ポジションを作成する | /lp/create | V3, V4 |
| 全レンジ(従来型)ポジションを作成する | /lp/create_classic | V2 |
| 既存のポジションに流動性を追加する | /lp/increase | V2, V3, V4 |
| 流動性の一部を割合で取り除く | /lp/decrease | V2, V3, V4 |
| 蓄積した取引手数料を回収する | /lp/claim_fees | V3, V4 |
| 最新のプール状態を読み取る | /lp/pool_info | V2, 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 は既存のポジションの順序と一致している必要があります |
nftTokenId | v3/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の整数です |
nftTokenId | v3/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 ID | tokenId | nftTokenId | v3NftTokenId (整数) |
| 概念 | /lp/create | /lp/increase |
|---|---|---|
| v4 permit ペイロード | batchPermitData | v4BatchPermitData |
承認と 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 | レート制限 | 指数バックオフを行い、繰り返しの読み取りをキャッシュします |
| 500 | API エラー | バックオフして再試行します |
| 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 の利用可否または対応チェーンのドキュメントで確認してください。
追加リソース
- LP API:はじめに — 公式の概要
- LP API:統合ガイド — 公式ガイド
- Uniswap Developer Platform ダッシュボード — API キーを取得できます
- swap-integration — Trading API によるトークンスワップ用の関連スキル
- 高度なパターンのリファレンス — 許可の詳細解説、移行、NFT ポジション特有の注意点、信頼性