⛓Solanaのメジャーな🏢DEX Aggregator -> いろいろやりすぎてる.

info

Jupiter API

2025からv2になってAPI認証が必要になった. なおFree PlanはAPI Key不要.

🔌Jupiter Price API

https://station.jup.ag/docs/api/price

価格取得のAPI. quote apiだと1つのペアしか計算できないが, price apiだと100銘柄を指定可能.

  • idsで指定するのはbuy側. カンマで区切って複数指定可能. mint addressも可能.
  • vsTokenはなにも指定しないとUSDCになる.

たとえば, inputMint=SOL, outputMint=mSOLの場合は以下ようになる.

ref. Price API: Get on-chain price for any token | Jupiter Station


  • 取得できる価格がリアルタイムでない気がする. Jupiterがrouting計算のためのキャッシュしている情報から計算しているようだ.
  • 🔍Birdeye APIや🏢Pyth Network($PYTH)から価格取得をしたほうがいい.

🔌Jupiter Token List API

Solana界隈のトークンアドレスの情報取得 API endpoints.

list token format

取得できるlistのformatは@solana/spl-token-registryに従う.

strict and all lists

tags

  • “old-registry”
  • “community”
  • “wormhole”

🤔Jupiter token-listのallには同一シンボルの別アドレスがたくさんあるので注意

ちょっと調べただけで150コがsymbolとaddressが一意でなかった. 冒険したくなければstrictを参照するのが無難.

SOLとかUSDCに謎トークンがあるのは怪しすぎる.


token listのextensionsのisBannedのフラグをみると判定できる.

ref. https://station.jup.ag/docs/token-list/token-list-api


jupiterのlistを発見した. fake SOLってなんだよ!

https://github.com/jup-ag/token-list/blob/main/banned-tokens.csv

🎴ハニーポットトークン

🔌Jupiter Swap API

いわゆるAggreagaterのSwap機能. swapそのものだったり手数料はtaker/taker feeとしばしば呼ばれいてる.

Client API

nodejs

rust

非公式?本家からlinkされているわけではない.

GET/quote

Jupiter Aggregatorのコア機能がswapするルートの検索であるこのAPIといえる. 4hopくらいの複雑なルートでの最適なルートが取得できる.

  • swapMode: exactInは指定したinputMintのamountにそろえて計算, exactOutはoutputMintのamountにそろえて 計算.
  • onlyDirectRoutes: routePlanでpercent=100になる. hop数はこれでは制限できない. 勘違いしてた.

POST/swap

swap-txを取得.


  • useSharedAccounts=falseでSharedAccountsRoute CPIではなてくRoute CPIを利用するようになる. または, これによって自分のwalletに紐づくtokenのATAがないときはcreate ATAのtxも追加されるようだ. <2024-02-07 Wed 11:07>
  • <2024-01-18 Thu 13:24> market.cacheにないLPを指定してもエラーする.

Using maxAccounts

Transaction simulation failed: Transaction locked too many accountsのエラーを回避するために設定する.

上限は64(“error”: “max accounts specified is larger than maximum allowed 64”).

ref. https://station.jup.ag/docs/v6/swap-api#using-maxaccounts

feeを支払ってオンチェーンでの優先度をあげる

💡Compute Budgetについて. feeを支払ってオンチェーンでの優先度をあげる.

DAppsの画面からだと, auto/normal(0SOL)/High(0.000005SOL)/Turbo(0.0005SOL)の4つの選択肢がある.

swat tx APIの computeUnitPriceMicroLamports というパラメータ. なにも指定しないと, High(0.000005SOL)になるよう. これはJupiterではなくSolanaの仕様.

The compute unit price to prioritize the transaction, the additional fee will be computeUnitSet (1400000) * computeUnitPriceMicroLamports.

ref. https://station.jup.ag/docs/apis/swap-api#setting-priority-fee-for-your-transaction


prioritizationFeeLamports = auto: we use the RPC call getRecentPrioritizationFees to get the 75 percentile of all the write accounts involved and multiply it by 2. this is usually enough to outbid everyone but sometimes during high congestion, it may not be enough.

dynamicComputeUnitLimit = true: this will help as well. by default, the Jupiter API assumes that each swap will take up 1.4m compute unit. by setting it this way, it will adjust the compute unit to be dynamic. we run a simulation to estimate the compute unit the swap will take then we add an extra 40% margin. by having lower compute unit used, we can set a higher priority fee and it will help to get transaction through since now you are bidding on a higher priority fee.

API rate Limit

response messageから判断する. どうも負荷によって動的に代わるように思う. しょっちゅう変更されている. ドキュメントはあてにならない.

ref: https://station.jup.ag/api-v6#rate-limit

AMM was not found, amm_key: xxxxxxx

このエラーは存在しないamm_keyがswap routeにふくまるれというもの.

market.cache情報は自動更新されるのでしばしば発生する.

jupiter swap slippage/price impact

slippageの単位は📝ベーシスポイント(bp).

Slippage occurs when market conditions change between transaction submission and verification. The slippage rate is an important setting to prevent users from receiving fewer tokens than expected. If the price falls below the slippage rate, the transaction fails.

解説文ではfewer tokens than expectedとある, これは乖離が減る方向だけだろうか? いいかえれば 指定したoutAmountよりも小さくなる場合だけ?

indexed-route-map

事前にindexされたpathの情報.

api: https://quote-api.jup.ag/v6/indexed-route-map?onlyDirectRoutes=true

あるmintとそれに近接する別のmintの情報を事前に構築しておくことでpathのroute探索を行う.

{"mintKeys" ["xxx", "xxx",.. ]
 "indexedRouteMap"
  {"1" [1,3, 4]
   "2" [1,3, 5]
   ...
  }
}

tip: Jupiter Swapのroute/quoteをローカルで作成する

たとえばroute計算をquote apiではなくローカルで計算してその結果でtxをJupiter v6に送信したいとき, quoteの情報を自分で構築することで, Programを実行できる.

🔧jito-labs/mev-botではJupiter v4 でやっているので, 同じことはv6でもできた.

Jupiter Swapの変遷をしるにはjupiter-swap-apiのchangelogがよさそう

<2024-05-16 Thu 12:24> Jupiterが対応するものがSolana業界標準になっている今日このごろ.

https://github.com/jup-ag/jupiter-swap-api/releases/

🔧Jupiter market.cache

ref. https://cache.jup.ag/markets?v=4 (アクセス注意, 24MBくらいダウンロード走る).

<2024-12-05 Thu 15:53> 24/10からアナウンスなくv=3からv=4になってた.

Jupiter is periodically caching active and liquid markets

The market cache is an Array<AccountInfo<string[]> & {pubkey: string, params?: any}>. params are specific to each AMM and the expected fields can be found in the constructor of @node_modules/@jup-ag/core/dist/lib/.

おそらくJupiterが監視しているLPの情報. accountInfoとATA addrsssのJSON, これが10000以上ある.

{"pubkey":"CMmzv9Zr9JsboqciWPqXUsw3WK1aSJV5GVHjuimxQgfn","lamports":3145920,"data":["binaryなので省略","base64"],"owner":"FLUXubRmkEi2q6K3Y9kBPg9248ggaZVsoSFhtJHSrm1X","executable":false,"rentEpoch":0,"space":324,"params":{"addressLookupTableAddress":"6Y7sAeJ3LCTfcrk4jAzLBEmoaNMzKQdDB5m2gJ5G95LC"}}

Adding Your Own Custom Markets to Jupiter | Jupiter Station, legacyページに説明あった.

これはjupiter-coreで使われているProtocol(各DEX)ごとのinput情報になる. たとえばRaydiumAmmのSerumMarket情報は,

"serumBids": "F7G6Uvk6pRT3PnhZLm5YGA4GGTWHE8AfrdTVqyPc9fuC",
"serumAsks": "83tUsZUQeXDZR7uhtWHin2epPdjMNPtdyxCNgcFyhaFS",
"serumEventQueue": "5SVX8MeCrxVjWazeD5oeWjYBurHvygpma4NyTi9hmGws",
"serumCoinVaultAccount": "9CgWdLCkkVY1Mi95c8p3oKvCM7Fu3JQG9EBmRPvgBfoX",
"serumPcVaultAccount": "4mTDtiiSvjeqEYJcQVfDgbKBVJNpwjSyTc58q7GdB8Vo",
"serumVaultSigner": "2c7BqzhwBCb86DprTKte7UfK6F4Xwu4B1uX7YsWZ4c7G"

<2024-01-27 Sat 12:56> JSP 23:00にRaydium CLMMのinvalid なtickが除去された? なにかのチェックロジックが走ったのか?

データ形式の分析

pubkeyをgetAccountInfoで取得できたもの + 追加情報(params).

  • data: pubkeyをget-account-infoで取得したdataをbuffer->base64でencodeしたもの.
  • owner: DEXなどのUUID
  • pubkey: poolのUUID
  • params: DEXごとの追加情報.
    • addressLookupTableAddress: TBD, これはどこから作成している?

Jupiter market.cache/token-list allの仕組み

以下の3つの条件を満たすと数分でallにキャッシュされる仕組み. また, 🔌Jupiter Token List APIのallにも表示される.

;; https://station.jup.ag/docs/get-your-token-onto-jup

  1. Your token must exist on-chain and have token metadata conforming to the Metaplex Token Metadata.
  2. Your token must have at least $250 liquidity on both buy and sell sides.
  3. The buy and sell price impact shouldn’t be more than 30% as well. This is to prevent single-sided liquidity markets.

🌐Metaplex

9RAufBfjGQjDfrwxeyKmZWPADHSb8HcoqCdrmpqvCr1g

新規流動性プールが追加されると AccountAssociateTokenの作成とLookupTableへの追加を行う.

https://solscan.io/account/9RAufBfjGQjDfrwxeyKmZWPADHSb8HcoqCdrmpqvCr1g

extendLookupTableはあまり最適化を考えずに必要なaddressは重複を許して全部突っ込んでいるようだ.

🔧Jupiter Self-hosted swap API

利用にはDedicated RPC nodeが必要. 最も易そうなfly.ioだと最低 400くらいかかるか? Tritonだと1500/month, みんな金持ちなの? 富豪でないとつかえないじゃないか…?!

options

  • —market-cache: AMMを限定できる? 利用にはデータを取得してローカルで加工が必要そう.
  • —rpc-url: gRPCではなくRPC Nodeで起動.
    • gRPCはDedicated RPC Nodeが必要だが富豪しか無理.
  • —snapshot-poll-interval-ms: polling間隔を指定.
  • —enable-external-amm-loading: Enable loading external AMMs from keyedUiAccounts in swap related endpoints.
  • —allow-circular-arbitrage: input/outputが同じquoteを許可.

market.cacheをinputにつかう

加工して —makert-cacheのoptionに指定.

$ curl "https://cache.jup.ag/markets?v=3" -o market-cache.json
jq 'map(select(.owner != "srmqPvymJeFKQ4zGQed1GFppgkRHL9kaELCbyksJtPX"))' market-cache.json > market-cache-no-openbook.json

server side codes

server sideのコードはこれ?

https://github.com/jup-ag/jupiter-quote-api/tree/main

Jupiterのサーバ自体がfly.ioにhostingされているようだ. quote apiは requestをうけたら内部DBにgraphQL/redisに問いあわせ?

https://github.com/jup-ag/jupiter-quote-api/blob/10f1ea6fe778799d01b738c16c4b705cf32875fc/src/api.ts

RPC Nodeでつかう(監視するLP数を制限/polling間隔の調整)

—rpc-urlを指定すると gRPCkではなくRPCで起動できるもののdefault設定だとgetMultipleAccountsInfoを200msに900回くらいcallするのでまともに使えない.

—snapshot-poll-interval-ms でpollingのintervalゆるやかにして, —market-cacheで価格取得するammを限定することで 機能制限的にRPC Nodeでも利用はできる.


以下, 作業ログ. <2024-01-05 Fri>

  • RUST_LOG=debugで起動. traceだと多いな.
  • 起動で10000くらいのaddressを取得, これはmarket cacheの数と一致した.
    • jupiter_core::address_lookup_table_cache] Update 1988 ALTs into the AddressLookupTableCache in 3.362220901s
    • 10588 amms for 10587 lookup tables
    • おそらく起動時にLP情報を取得しておく.
[2024-01-05T05:30:16.330761Z INFO  jupiter_core::amms::loader] Loaded 10588 amms from market cache                               [2024-01-05T05:30:16.349450Z INFO  jupiter_core::amms::loader] {"Saber": 67, "Jupiter LO": 1, "Aldrin V2": 27, "Orca V1": 15, "Meteora DLMM": 20, "Cropper": 24, "Sanctum": 54, "Saros": 21, "Marinade": 1, "Penguin": 11, "Balansol": 6, "Lifinity V1": 8, "Helium Network": 2, "Raydium CLMM": 143, "Whirlpool": 628, "Oasis": 10, "Aldrin": 15, "Perps": 1, "Token Swap": 35, "StepN": 3, "Bonkswap": 12, "Lifinity V2": 21, "Crema": 10, "FluxBeam": 181, "Openbook": 50, "GooseFX": 3, "Meteora": 388, "Phoenix": 9, "Orca V2": 118, "Invariant": 19, "Saber (Decimals)": 17, "Raydium": 8655, "Mercurial": 13}
  • jupiter_coreはおそらく非公開. interfaceのみが公開されているのでそれのcall元.
  • RaydiumとWhirlpoolがダントツで多い?
  • 1分半くらいなにも通信しなかったがその後getMultipleAccountsを呼びまくる, 1回で10アドレス.
    • alchemy無料枠で429 Too Many Requestsで失敗で動かなくなる.
[2024-01-05T05:32:34.900276Z INFO  jupiter_core::router] Perform initial update #1
[2024-01-05T05:33:28.119222Z INFO  jupiter_core::price_tracker] Price tracker updated 8890 prices in 72.762496ms
  • RPC Node だとinitial update関数が定期的に呼ばれる.
  • 8890 prices がupdateされる. 1000くらいは価格変化がない?
    • Price tracker updatedがログメッセージ.
    • snapshot-poll-interval-msで指定した間隔でupdate
    • getMultipleAccountsで10なので1回のintervalで890のAPI が呼ばれる.

🔌Jupiter CPI

チェーンにdeployされて公開されているJupiterのスマートコントラクト, 及びそれを組み込む方法.

🔌Solana CPI

Jupiter Aggregator v4

Jupiter Aggregator v6

Route instruction

routeを実行するための関数は4つある.

  • route
  • routeWithTOkenLedger
  • sharedAccountsRoute
  • sharedAccountsRouteWithTokenLedger

Shared Accounts Route Instruction

routeはswapするwalletのATAを必要とするが, sharedの場合はJupiterの管理するaccountを利用. これによって中間path上のtokenに対応するaddressを自分で用意する必要がない.

route instructinとの差分は,

  • arguments: id, 0,1,2… などの番号.
  • accounts:
    • programAthority: idに対応するaddress
    • programSourceTokenAccount: idに対応するaddress
    • programDestinationTokenAccount: programSourceTokenAccountと同じ
    • sourceMint
    • token2022Program: JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4

idとそれに紐づくwallet及びATA addressの3つがセット.

  • id: 4
    • owner: 4xDsmeTWPNjgSVSS1VTfzFq3iHZhp77ffPkAmkZkdu71
    • ata: 6zAcFYmxkaH25qWZW5ek4dk4SyQNpSza3ydSoUxjTudD(wSOL)

sharedはrouteの前にToken Transaferが走り, 自分のwallet addressからjupiter管理のwalletに転送されてから処理が始まる.


以下推測. Routeではinstructionsのはじめと終わりにtoken Transferが走ってないように思う.

route swapを実行する関数. UIやAPIからswap-txを取得するとSharedAccountsRouteとなり, Routeではないことに注意. おそらくsharedとはJupiterの管理するwallet及びATAsで, 自分で財布を用意しなくても途中経由するrouteのトークンに対応する財布がつかえるみたいな.


<2024-02-05 Mon 15:00>, Shared Account RouteでのAtomic arbはできなくなった.

Input and output mints are not allowed to be equal

ref. 💭Jupiter API をつかったAtomic Arbはできなくなった(24/02/05)

Arguments

instruction dataとして表示されているもの.

routePlan

routePlanの中身の解析は🔍Solana FMが見やすい.

  • inputindex/outputindexはrouteplanにおけるリストのindex.
    • 1hop: inputindex=0, outputindex=0
    • 2hop: [{inputindex=0, outputindex=1}{inputindex=1, outputindex=0}]
    • 3hop: [{inputindex=0, outputindex=1}{inputindex=1, outputindex=2}{inputindex=2, outputindex=0}]
[{"swap":        {"raydium" {}}
 "percent":     100
 "inputIndex":  0
 "outputIndex": 1}
{"swap":        {"raydiumClmm" {}}
 "percent":     100
 "inputIndex":  1
 "outputIndex": 0}]
  • “whirlpool” [{“name”:“aToB”,“type”:“bool”}]
    • このbool値はLPの定義に合わせる. SOL-BonkのLPに対するBonk->SOLのswapはfalse.

Accounts

  • Input Accountsとして表示されてるもの.
  • IDLに定義されているacccountsと remainingAccountsとして渡されたものが合わさっている.

route

  1. tokenProgram
  2. userTransferAuthority
  3. userSourceTokenAccount
  4. userDestinationTokenAccount
  5. destinationTokenAccount
  6. destinationMint
  7. platformFeeAccount
  8. eventAuthority
  9. program

remainingAccounts

各DEXのswapのinputとなる情報のリスト. Inner InstructionのSwapに必要なアカウントのリスト.

Anchor lib(Context)の仕様に合わせてacccoutsと一緒にremainingAccountsとsignersを指定する.

  • remainingAccounts
    • inner CPIのためのAccountsのリスト.
    • 内容は各DEXのswap Programによる.
  • signers: payers(keypair)のリスト.

ref. https://coral-xyz.github.io/anchor/ts/index.html#Context


get-swap-ixでJupiter sererから取得した情報をみると, はじめの13 accouunsがIDLに定義されているもので, 残りが各DEXのCPIを実行するためのinputとなるaccoutsということがわかる. Jupiter は各DEXのswap programを呼び出してつないでいるので, 各DEXのCPIのためのaccoutsはそれぞれのDEXごとの仕様による. GitHubでprogram_idを検索してみるとすぐでてくるかも.

Raydium v4

🪣Raydium AMM Pool v4

Raydium CLMM

🪣Raydium CLMM

各MarketのProgramのlegsの末尾にJUPのprogram account metaをつける

Cross-program invocation with unauthorized signer or writable account

<2024-05-31 Fri 15:14> 完全にhackだが, Meteora DLMM対応で解った. meteoraDLMM-Xではエラーになる, X->meteoraDLMMでは問題ない.

おそらく, remainingsに可変数のパラメータを与えたあとに区切りを示すためにJUP6のProgramのAccountMetaを付与する必要がある.

Address Lookup Table Accountsは?

📝Address Lookup Tables/ALTをどうするか?

🔧Jupiter market.cacheのparamsにあるaddressLookupTableAddressが設定されている. 1AMMに対して1つのアドレスなので, たとえば4AMMを経由すると4つのアドレスが設定されている.

独自のLUTを追加するときはpushするようだ.

;; https://station.jup.ag/docs/additional-topics/composing-with-versioned-transaction#using-your-own-address-lookup-tables

if you’d like to use your own address lookup tables, you just need to append your tables to the address lookup table accounts

addressLookupTableAccounts.push(yourAddressLookupTableAccounts)


さらにflash-fillをつかったサイズ圧縮方法もある.


jitoの場合, onchainにあるaddress lookup tableを読み込んで使えそうなaltをキャッシュしておくという力技をやっている.

;; https://github.com/jito-labs/mev-bot/tree/master ;; https://github.com/jito-labs/mev-bot/blob/master/src/lookup-table-provider.ts

However, there’s a constraint with jito bundles: a transaction in a bundle cannot use a lookup table that has been modified in the same bundle. To work around this, the bot caches all lookup tables it encounters in txns from the mempool in the lookup-table-provider.ts and then selects up to the three lookup tables that decrease the transaction size the most. This solution works well, especially after the bot has been running for a while.

Examples

🔌Jupiter Trigger API

Jupiter Trigger Swap/旧Jupiter Limit Order.


Program

https://solscan.io/account/jupoNjAxXgZ4rjzxzPMP4oxduvQsQtZzyknqvzYNrNu

  • initializeOrder: 新規limit order注文.
  • fillorder: openOrderされたlimit注文を閉じる.

Dashboard

birdeyeがUSDC/SOLのLimit Orderの状況を可視化するページを作成した.

sdk

公式ドキュメントはないので推測を含む.

sdk: https://www.npmjs.com/package/@jup-ag/limit-order-sdk

  • getOrders(): open orders取得. https://jup.ag/api/limit/v1/openOrders
  • fillOrder(order_id): keeperのためのif.
    • owner: 自分のwallet pubkey
    • orderAccount:
      • publicKey:
      • account: limit order object.
        • これはどうやらsdkで取得したobjectが良さそう.
        • httpで取得したものはmaker, makerOutputAccountの属性がない.
    • amount: inAmount/makingAmount
    • expectedOutAmount: outAmount/takingAmount

httpとsdkで変数名が微妙に異なる.

  • inAmount/makingAmount, oriInAmount/oriMakingAmount
  • outAmount/takingAmount, oriOutAmount/oriTakingAmount

手数料はUSD-stable/SOL-stableとその他で異なる

ref. https://github.com/jup-ag/limit-order-taker-example/blob/main/src/fee.ts

sdkのgetFee()で取得できる.

  • makerFee: 0
  • makerStableFee:0
  • takerFee: 0
  • takerStableFee: 20bp ~ fillOrderの手数料は0.2%

  • USD-stable
    • USDC
    • USDT
    • USDH
    • USDCet
    • USDTet
    • PAI
    • UXD
  • SOL-stable
    • SOL
    • stSOL
    • mSOL
    • bSOL
    • jitoSOL

InvalidMaxTakingAmount/InvalidMakingAmount

なにがInvalidなのか調査中… Programが公開されてるわけではないのでなぜInvalidなのかわからない.

🔌Jupiter DCA

大きな量の注文を数十秒に1回intervalに分割しつつswapする機能.

📝プライスインパクト(Price Impact)📝サンドイッチ攻撃対策.


これは完全にフロントランニングを防げるわけではない. そもそも流動性がスカスカなプールでswapしようとするならば防げない.

🔌Jupiter Perpetual Exchange

PERP DEX.

https://solscan.io/account/PERPHjGBqRHArX4DySjwM6UJHiR3sWAatqfdBS2qQJu

🪣JLP Pool

JLP(Jupiter Liquidity Pool). Jupiterの提供する🎴流動性マイニング.

rewardは直接LP Tokenとして受け取るのではなくてJLP Pool全体に再投資される結果JLPの価格が上がる.

🪣Multitoken Pools

$JLP

Poolに資産を預けたときの🪙LP Token.

  • JLP token value are:
    • An index fund of SOL, ETH, WBTC, USDC, USDT.
    • Trader’s profit and loss
    • 70% of the generated fees
  • 流動性の提供と解除は単に 対象トークンとJLPをJupiter Swapで交換するだけ.

$JLPはSolanaにおける主要トークンのインデックスファンド

$JLPはSolanaにおける主要トークンのインデックスファンドといえる. したがってSOL/ETH/BTCがbull marketのときはパフォーマンスが悪い.

An index fund of SOL, ETH, WBTC, USDC, USDT.

JLP is denominated in USD. During a bull market, JLP may not outperform SOL, ETH, or BTC. It is suggested to consider swapping stable tokens (USDC/USDT) into JLP to potentially benefit from yield generated by perpetual trading activities. Please note that this is not financial advice.

  • 23/11/09に0.1 SOLをいれてみたら24/04/05に0.05 SOL ??? 減ってる気がした.

🐵Jupiter ApePro

https://ape.pro/

ミームコインexplore.

Smart wallet

market orderを実行.

自分のwallet addressでログインしても、別のアドレスに0.1SOLを入れてトレードしてくださいと出る. addressはJupiter管理のようだ.

ApePro Limit Order

limit orderを実行.

preFlashApproveの末尾のアドレスをportfolioで検索するとひっかかる. leaderboardは上位50しか表示されているのでみんな儲かっているように思うが、損している人がきになる.

Journals

  • <2025-01-08 Wed 11:25> いじってみた. 0.03 SOL損した. これはひどい. 新規トークン上場で6つのbuy order/5つのsell order, あれ気づいたらポジションあるの自分だけじゃん?ってなった. 4人が得をし2人が損をしたクソmeme coin trade.
  • <2025-01-08 Wed 13:46> これ、feeが高すぎるな… market order通すのに1000円も払いたくない…
  • <2025-01-08 Wed 14:01> 再び挑戦して0.05SOL損して撤退. 0.1SOL入れたのに2回で0.01になっちゃった. 3000円くらい草コインパチンコで損した.

Topics

Jupiverse

https://jupuary.jup.ag/dashboard

<2025-02-08 Sat 11:54> 統一あかうんと?

Jupiter Lock

税金対策?

https://station.jup.ag/guides/jupiter-lock/jupiter-lock

Routing

  • Jupiter DEX Routing(Aggregator Swap)
    • 旧来の方法. ベルマンフォードで最適ルート検索.
    • Jupiter誕生時の元々のProtocolはこれ.
  • JupiterZ

🪙JupSOL

Jupiterの提供する🪙LST.

https://station.jup.ag/guides/jupsol/jupsol

🔌Jupiter Bridge

  • onramp(法廷通貨)
  • Circle’s CCTP(USDC only)
  • 🏢Wormhole

🔌Jupiter FlashFill

📝フラッシュローンのようなもの.

🔍Jupiter Stats

アクセス制限はあるとは思うがいろんなSolana情報を可視化表示するページ, 及びAPI endpoints.

Jupiter Referral Program

Jupiter Supported DEXes/Protocols

Jupiter の support dexまとめ. LPを提供しているのはDEXだけではないのでProtocolと表現するのがいいのかも.

Jupiter DEX SDK

JSのjup-ag/coreはdeplicatedになったものの, そのサブモジュールともいえる各DEXのSDK libはまだある. jup-ag/coreは中でこれを呼び出している.

  • たぶんdeplicatedの理由はJSではなくRustに完全移行したいから.
  • 🔧jito-labs/mev-botではこれを利用してローカルでgetQuoteをしている.

jup-core/sdk

jup_core/rust integration

Rustのjup_coreはどうも非公開. interfaceの組み込み方法だけ見つかる. TypeScriptはcoreから派生したサブモジュールのsdkたちはGitHubにあるがいつ非公開になるかはわからない.

ただし, implementationをしている各取引所がその実装を公開してJupiterがそれをforkしている場合はよくある.


js integration(jup-ag/core)

おそらくJS Dex Integration機能を担うモジュールがcoreだったが, NO longer supportedになった(2023). jup-ag/api に移行してといいつつ, これはclientでありサーバサイドではない.

JSではなくRustをサポートする方針のためJSは保守しないということ.

今GitHubにあるcoreで使われているモジュールはどうなるのか? GitHub にはRustのsdkはないようだ.

このライブラリの使い方は🔧jito-labs/mev-botのコードが参考になる.


Amm SDK Class

各Protocolのinterfaceの実装をどう呼び出すか.

  1. constructor
    • AMMオブジェクトを生成する.
    • optionのinputはmarket.cacheの情報をつかう.
  2. getAccoutsForUpdate
    • 価格の更新に必要なaccount addressのリストが取得できる.
    • このアカウントをgetMultipleAccountsInfoによって更新する.
  3. update
    • getAccoutsForUpdateのaddressをkey, updateしたアドレス情報BufferをvalueにしたMapをインプットにしてAmmオブジェクトにsetする.
  4. getQuote
    • 価格を計算する. ここでは通信は発生しない.

🤖Jupiter Atomic Arb

🔌Jupiter Swap APIをつかった🤖Atomic Arbitrage.

<2024-02-06 Tue 10:27>, 🔌Jupiter CPIによるRouteをつかった方法, またはgeSwapTxでuseSharedAccounts=falseを指定する.

ResponseError: Response returned an error code, status=500, detail={“error”:“Usage of shared accounts is prohibited for arbitrage transactions, set useSharedAccounts to false”}


A->BとB->Aでルートを見積もって, 価格差があれば A->B->AでAtommic Swapを実行. JupiterのAgregatorとしての最適ルート選択機能によって4hopのような複雑なルートで(自前でルート計算してなくても), Atomic Arbitrageができる.

API v2(jup-core)だと, inMint/outMintが同じものを指定すると, いい感じに中間トークンを経由したルートを取得することができた. しかし, v4以降では仕様変更によりそれはできなくなった. v6もできなくなった(24/02/05).

Discordを覗くと, それはspam arbs対策, つまり中の人(JupJup)はArbitrage目的でAPIを使われることはあまりよいとは思ってないように感じた, というよりけっこう嫌がってる(わたしの感想).

❓2つのJupiter Swap Txをcomposeして1つのAtomic Swapにするには?

たとえばUSDC->SOLとSOL->USDCの2つswap見積もりをした結果利益が出た場合, 2つのswap txを1つにまとめてAtomic に送信したい, ie. 📝Atomic Swap.

deplicatedなjup-coreのgetQuoteだと, inMint/outMintが同じでも見積もりしてくれたが, v4やv6のgetQuoteはその機能はない(arbitrage目的のAPI利用を嫌がり仕様変更された).


Refs

Input and output mints are not allowed to be the same

Discordを漁ると古いv2の情報, jup-coreがででくる.

v4から, inputMintとoutputMintが同じだとエラーするようになったが, それ以前は可能だった. なのでv2では, getQuoteにin/outで同一Mintを指定することで, Atomic Swapのrouteを求めることができた. 今はできない. arbitrage spamを止めるための仕様変更らしい…


v6でも禁止された.

💭Jupiter API をつかったAtomic Arbはできなくなった(24/02/05)

<2024-02-06 Tue 10:25> メッセージが変わって明確にarbはだめだよとのこと. shared route CPIではなくroute CPIを使ってくださいとのことなので, 逆にいえばRoute CPIはarbを許可されている.

ResponseError: Response returned an error code, status=500, detail={“error”:“Usage of shared accounts is prohibited for arbitrage transactions, set useSharedAccounts to false”}

swapTxのLUT addressから解決できるLUTのサイズを削減することはできる?

answer: quote APIのoptionsのmaxAccountsである程度は削減できる. ただそれでも多い場合もあるので困ってる.

ref. https://station.jup.ag/docs/v6/swap-api#using-maxaccounts

Addressに重複があるような記述がドキュメントにあるので, addressの重複を排除すればよいかもだけどたいへんそうなのでやってない.

The maxAccounts is an estimation since it doesn’t consider account overlapping but it is a good start to control how many accounts you want per transaction.


Accountが多すぎると容量オーバーになる.

reason:Error: failed to send transaction: Transaction simulation failed: Transaction locked too many accounts

  • <2023-09-21 Thu 10:20>
    • 単純に2つのquote見積もりをmergeしてもTxの容量オーバーになる.
    • 単純に取得した情報だとAddreeLookupTableに重複情報が含まれているならば重複排除で圧縮が必要.
      • maxAccountsで調整できるかも.

2つのroutePlanをcomposeして1つのSwapにまとめるには?

answer: これは単純に配列データをconcatすればいい.

❓quoteは独自で実施してswap目的だけでProgramを呼び出すには?

jitoのコードが参考になる.

❓鞘(価格差)とslippageBpsの関係は?

slippageBps=0だとなかなかTxが通らない. たとえば価格差が大きい時, slippageBpsを増やすことである程度の損失を形容しつつtxを通すことはできるか?


(価格差%) > (hop数) x (slippage%) ならばいけるか?

たとえば USDC->SOL->USDCの閉路スワップを考える. この場合2hop. get quoteに設定するslippageBpsを0.01%にすると, 合計slippageは 0.02%. 1ドルをswapしようとしたとき, 1.0002ドル以上ならばよい?


<2023-09-21 Thu 13:55> Get/quoteのslippageBpsの値を変更してもroutePlanに影響がでるわけではないので, 価格差を考慮してquoteResponseを書き換えることが可能か? -> 多分できた.


Price Impactを考慮したな最適なinAmountはどうやって算出する?

DEXにおけるamountはCEXの文脈では📝ロットと言い換えることができる.

computeUnitPriceMicroLamports

大きな価格差を見つけた時, priolityを上げる.

https://station.jup.ag/docs/v6/swap-api#setting-priority-fee-for-your-transaction

💡JupiterでTradeするときはSOLよりもwSOLのほうが高速

wSOLのガイド記事からの情報. とくに頻繁にやりとりするトレーダーは, 🪙wSOLがいい.

You can now use wSOL (wrapped SOL) to trade directly with any SPL token on Jupiter! This makes Jupiter even faster and more convenient for traders who trade frequently with SOL, since it avoids having to wrap/unwrap SOL, which might result in multiple transactions when the routes are complicated.

UIからwSOLをenableするには設定でwSOL modeの有効化が必要.

swapのときにMint addressで指定するのは実際にはwSOLのアドレスだが, Jupiterの場合は wSOL modeを有効にしない限り, SOLのアドレスとしてwSOLを表示してさらに内部でJupiter Programがwrap/unwrapをしている. そのため, ユーザからみるとSOLをつかって取引をしているように見える. 実際のところはJupiterではwSOLを内部で使っている.

txを比較すると具体的には, 3つのinstructionが省略される.

  • Token ProgramによるCreate Associated AccountでwSOL addressの作成.
  • SOL TransferでwalletのSOLから作成したwSOL addressに転送.
  • Token Programによる Close Acccunt でwSOL addressの削除

Wrapped SOL (wSOL) | Jupiter Station

✨Jupiter Atomic Arbのエッジは4hopでありUSDC/USDT/SOL以外のトークンペアのプールを経由するから

Jupiter quote APIで見積もってtxを発行するだけのDDoSでもちょっと利益がでるのは, 4hopだから. Atomic Arb自力計算だと2hop, 3hopが主流で4hopは難易度が高い.

4hopを通ろうとすると, USDC/USDT/SOLを含まないペア通しのプールを経由することがとてもおおく, ここにエッジがある. 逆に言えば, ニッチなプールを経由するようなrouteでないとまったくJupiter Atomic Arbは素のAtomic Arbに対して勝ち目がない.

🤖Jupiter mmbot

🔌Jupiter Limit Orderをつかうmaker bot.

ref. Run a limit order bot with Javascript | Jupiter Station

🤖Jupiter Keeper bot

jupiter keepers, Keeper bot. Keeprとは 🔌Jupiter Limit Order機能の固有概念.

Keeper botは運営のみになった, 以下deplicated.


  • keeper bot/keepers
  • limit-order-taker bot

大口のLimit Orderはswapがすぐには実行されないので, LPとLimit Orderを監視して, その部分約定をすることで価格の乖離分のサヤをとるのがkeeper, という理解であっているか? DEX, つまり無人取引所においてはkeeperが価格乖離をインセンティブにマッチングを助ける.

;; https://station.jup.ag/guides/limit-order/how-lo-work#the-difference-between-jupiter-limit-order-with-clob

Once the order is placed, keepers will constantly monitor the liquidity landscape and execute the limit order when the market price reaches your limit price.

The keeper will monitor the price on-chain using Jupiter Price API & Birdeye API.

If the keeper detects that the on-chain price of $SOL hits 10 USDC, it will proceed to get execute and fulfill the order.

If the order size is too large (and there is insufficient liquidity on-chain), the keeper will try to execute the order with a smaller chunk order size and attempt to partial fulfill the order (to ensure the best price with minimal price impact), and will continue to attempt to do so until the order is fully executed.

Orders are executed by keeper, imagine keeper to be like liquidator in lending protocol, when price hit the limit price (including fees), keeper will execute the order.

アルゴリズム

ref. https://station.jup.ag/guides/limit-order/how-lo-work

If a Limit Order is placed to buy 1 SOL.

The keeper will monitor the price on-chain using Jupiter Price API & Birdeye API.

If the keeper detects that the on-chain price of $SOL hits 10 USDC, it will proceed to get execute and fulfill the order.

If the order size is too large (and there is insufficient liquidity on-chain), the keeper will try to execute the order with a smaller chunk order size and attempt to partial fulfill the order (to ensure the best price with minimal price impact), and will continue to attempt to do so until the order is fully executed.

Executed order (or any partial amount) will be transfer directly to your wallet automatically.

Orders are executed by keeper, imagine keeper to be like liquidator in lending protocol, when price hit the limit price (including fees), keeper will execute the order. After the order executed, the user will get exactly what he quoted for including the platform fees by Jupiter.


ref. https://github.com/jup-ag/limit-order-taker-example

  1. Get all the open orders from onchain, filter and sort the orders according to the price.
  2. Get quote from Jupiter Swap API and check whether the order is profitable to execute.
  3. Bundle Jupiter Swap Ix with Limit Order Ix to execute the order.

価格監視

quote apiだと1銘柄しか価格が取得できない.

The keeper will monitor the price on-chain using Jupiter Price API & Birdeye API.

わたしはbirdeye APIで全銘柄を取得して監視してた. Pythから価格取得してもよかったかも.

CLOB通貨ペアにおける基軸通貨/決済通貨はAMMではoutAmount/inAmount

AMMとCLOBの対応関係. 基軸通貨/決済通貨とすると, outAmount/inAmount.

  • inAmount/決済通貨/counter currency
  • outAmount/基軸通貨/base currency

take limit order/fill order

makerの方向と同じ方向/同じinput Amountで, taker swapをする.


mSOL/SOL CLOBを考える(base=mSOL/counter=SOL)

https://birdeye.so/limit-order/So11111111111111111111111111111111111111112/mSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So?chain=solana

  • ask
    • makerは mSOL->SOLでlimit orderしている.
    • keeper(taker)は mSOL->SOLでswapして, SOL->mSOL でfillorderしたい.
  • bid
    • makerは SOL->mSOLでlimit orderしている.
    • keeper(taker)はSOL->mSOLでswapして, mSOL->SOLでfillorderして,

FillOrderにおけるalready closed判定(custom program error: 0xbc4)

先に別のkeeperによってFillOrderされた場合, あとからFillOrderすると発生する.

sdkによるgetOrder/getOrdersは速度チェーンの最新状況よりも遅いので, web3.jsのgetAccountInfoで判定する必要がある.

commitmentレベルはfinalizedよりもprocessedのほうが最新が取得できる. open->closedの方向しかないので不確定情報でもprocessedで取得したほうがいいかも.

Program log: Instruction: FillOrder Program log: AnchorError caused by account: order. Error Code: AccountNotInitialized. Error Number: 3012. Error Message: The program expected this account to be already initialized. Program jupoNjAxXgZ4rjzxzPMP4oxduvQsQtZzyknqvzYNrNu consumed 5346 of 1400000 compute units Program returned error: custom program error: 0xbc4

keeper address調査方法

  • limit order programの実行履歴をみる.
  • InstructionsがInitialize Order/Cancel Order以外をみる.
    • signatureをクリック.
  • Main Actions欄のIntract with programが実際のトークンの移動を示す.
  • FlashFillorder/FillorderのInstructionの#1 Orderが元となる指値.
    • このLimit Orderに対するSingatureの履歴から指値が刺されたtxやtakeされたtxがわかる.
    • SPL Token Balanceが0 SOLでなければまだ指値は生きている.

📈Duneのdecorded projectsのなかにJupiterのLimit Orderがあり, このテーブルをSQLで調査できる.

🔍Birdeye limit order(jupiter)

🔍birdeyeの機能のひとつで, Jupiterのlimit orderの指値状況を可視化している.

📈ヒートマップのように, 指値の厚みまで可視化しているのは賢い.

https://birdeye.so/limit-order?chain=solana

birdeye apiによる価格取得

Jupiterのdocにもbirdeye のapiが登場する.

🤔Jupiter Keeper botはAtomic ヒゲキャッチbotか

<2023-10-24 Tue 19:10>

最近, 🔍birdeye limit order(jupiter)という機能ができて, Jupiter Limit Ordersが可視化されたのが面白い. AMMには指値なんて概念はない. しかし, もやはこれはOrderBookと遜色ない.

https://birdeye.so/limit-order?chain=solana

さらに, 🤖ヒゲキャッチbotとの比較してみる. 通常, ヒゲキャッチは指値を板に出して大口注文でヒゲができるのを待ち構える. 一方keeper botは, 指値を出しているのはbotではなく一般の人たちで, なんらかの理由でヒゲが生じたあとにそのヒゲをatomic に狩りにいく.

フロントランニング/バックランニングの概念で解釈すると, ヒゲキャッチは大口が入ることを予測して待ち構えるフロントランニングに近く, 一方keepersは市場が動いたあとに仕掛けるバックランニング. さらにいえば, DEXにおけるAtomic性が特徴的だ. 取引は成功して利益を得るか, 失敗しても無傷か. all or nothing.

しかし, Jupiter Keeper botの致命的な欠点は, 人気がなくしたがって指値を指している人がいないということ. どうなるかは今後次第かも.

💡DeFi バックランニング

🤔Jupiter keeper botの闇(PreFlashFillOrder/FlashFillOrder)

公開されている関数はFillOrderで基本的にこれを叩くのだが, それとは別に2つの関数をつかっているbotが2つある.

  • PreFlashFillOrder
  • FlashFillOrder

これはJupiter運営のbotで, このIFを叩くことはできない.

keeper botの闇は運営のbot 2つが強すぎてほぼ利益取ってかれている気がしてならない. さらに, 運営だからOKなのか, 手数料度外視でDDoSアタックのように大量txを送りまくっている. まさか損する覚悟でbotを動かしているのか?


<2023-11-25 Sat 22:41> もっとヤバイことに気づいた. 運営は手数料を払っていない!つまり, Fee分のサヤ20bpsが有利. これ勝てるわけないぞ…


custom program error: 0x177a: InvalidKeeper

FillOrder Program実行不可.

Program log: Instruction: FillOrder Program log: AnchorError thrown in programs/limit-order/src/instructions/fill_order.rs:144. Error Code: InvalidKeeper. Error Number: 6010. Error Message: InvalidKeeper. Program jupoNjAxXgZ4rjzxzPMP4oxduvQsQtZzyknqvzYNrNu consumed 24549 of 1307328 compute units Program returned error: custom program error: 0x177a

Journals

refs

🔌Jupiter Ultra mode

API

https://dev.jup.ag/docs/ultra-api/

JupiterZ Routing(RFQ Swap)

Trigger Orderの流動性をつかったSwap. Keeper botのtaker注文の実行.

  • makerがいればそれをtakeするので通常のswapよりも安くswapできる.
  • ただしいつもmakerがいるわけではない. -> Ultra Swapで自動判定.
  • JupiterZ Routingは Ultra APIでのみ提供されるが、指定して実行はできない.
    • RFQ Swapが公開されないのはKeeper botが死んだ理由と同じ気がした.
    • spam keeper botが連打する可能性があるからか?
    • しかしこれには中央集権化の懸念がある.

<2025-03-31 Mon 12:16> たまにkeeper的なswapが実行されるのはこのせいか?問題はクリプタクトでこれが認識できるかということ. staketaxではできなかった.

🚦RFQ-based Protocols

RFQ Swapと Aggregator Swapでkeeper botのようなサヤ取りはできる?

get balance

walletのbalanceがトークンごとに全部とれた.

https://dev.jup.ag/docs/ultra-api/get-balances

Jupiter Trouble Shooting

Error Codeは600x, IDLに書いてある.

custom program error: 0x1771/6001 - slippage発生

https://station.jup.ag/docs/additional-topics/troubleshooting#wrap-and-unwrap-sol

Transaction simulation failed: Error processing Instruction 1: custom program error: 0x1771


0x1771 occurs when the Slippage tolerance is exceeded, so when the final out amount is less than the minimum out amount.

custom program error: 0x1793 - Oracle confidence is too high

Lifinity Swapで発生.

原因不明だが取引所固有のエラーかもしれないのでその取引所をquote route対象から除外する.

  • Oracle confidence is too low
  • Oracle confidence is too high

Program log: AnchorError occurred. Error Code: OracleConfidence. Error Number: 6035. Error Message: Oracle confidence is too high.

  • Invoking Lifinity Swap V2

custom program error: 6001

なに? Orca Swap Program v2で発生.

Runtime error: custom program error: 6001

InvalidCalculation. Error Number: 6002. Error Message: Invalid calculation.

Program log: AnchorError occurred. Error Code: InvalidCalculation. Error Number: 6002. Error Message: Invalid calculation. Program Jupiter Aggregator v6 consumed 211397 of 1379805 compute units

おそらくslippagebpsに設定する値が大きすぎると発生(9980を設定したら発生した).

custom program error: 0x15

おそらくin-amountが小さすぎると発生

Program log: Error: The fee calculation failed due to overflow, underflow, or unexpected 0 Program Swap Program consumed 19834 of 1256921 compute units Program returned error: custom programps error: 0x15

Insights

JupiterはSolanaのAggregatorといいつつほぼRaydium v4が実体

面白いインサイト. JupiterはSolanaチェーンにおけるAggregatorだが, 監視してるPoolは11000ちょっと, その内訳はほぼRaydium v4.

ただ一方で, Solanaチェーンで草コインではないまともなTVLを持つ数自体が100あるのかな?というのでなんともいえないか.

{"Saber": 67, "Jupiter LO": 1, "Aldrin V2": 27, "Orca V1": 15, "Meteora DLMM": 20, "Cropper": 24, "Sanctum": 54, "Saros": 21, "Marinade": 1, "Penguin": 11, "Balansol": 6, "Lifinity V1": 8, "Helium Network": 2, "Raydium CLMM": 143, "Whirlpool": 628, "Oasis": 10, "Aldrin": 15, "Perps": 1, "Token Swap": 35, "StepN": 3, "Bonkswap": 12, "Lifinity V2": 21, "Crema": 10, "FluxBeam": 181, "Openbook": 50, "GooseFX": 3, "Meteora": 388, "Phoenix": 9, "Orca V2": 118, "Invariant": 19, "Saber (Decimals)": 17, "Raydium": 8655, "Mercurial": 13}

🔗References