Jupiter

JupiterはSolanaの主要な流動性アグリゲーターであり、幅広いトークンとあらゆるトークンペア間の最適なルート発見を提供します。

Installation

@jup-ag/core は、jupiterオンチェーンプログラムと対話し、2つの可能なトークンペア間のスワップを実行するために使用されるコアパッケージです。

yarn add @jup-ag/core
npm install @jup-ag/core

Jupiterからのトークンリストの取得

特定のネットワークでJupiterとSwapできるすべての可能なトークンが取得されます。

Press </> button to view full source
import { Jupiter, RouteInfo, TOKEN_LIST_URL } from "@jup-ag/core";
import { Connection, PublicKey } from "@solana/web3.js";

interface Token {
  chainId: number;
  address: string;
  symbol: string;
  name: string;
  decimals: number;
  logoURI: string;
  tags: string[];
}

(async () => {
  const ENV = "mainnet-beta";
  const tokens: Token[] = await (await fetch(TOKEN_LIST_URL[ENV])).json();
})();

Jupiterインスタンスの読込

Jupiter インスタンスは、提供された設定で作成されます。インスタンスが受け取るオプションのパラメータは多数あり、詳しくはこちらopen in new windowをご覧ください。

Press </> button to view full source
import { Jupiter, RouteInfo, TOKEN_LIST_URL } from "@jup-ag/core";
import { Connection, PublicKey, Keypair } from "@solana/web3.js";

interface Token {
  chainId: number;
  address: string;
  symbol: string;
  name: string;
  decimals: number;
  logoURI: string;
  tags: string[];
}

(async () => {
  const ENV = "devnet";
  const tokens: Token[] = await (await fetch(TOKEN_LIST_URL[ENV])).json();

  const USER_KEYPAIR = Keypair.generate();
  const connection = new Connection("https://api.devnet.solana.com");

  const jupiter = await Jupiter.load({
    connection,
    cluster: ENV,
    user: USER_KEYPAIR, 
  });
})();

RouteMapの取得

RouteMapは、特定の入力トークンに対してどのトークンを交換できるかを識別します。ルートマップにはトークンミントアドレスのみが含まれ、メタデータは含まれません。

Press </> button to view full source
import { Jupiter, RouteInfo, TOKEN_LIST_URL } from "@jup-ag/core";
import { Connection, PublicKey, Keypair } from "@solana/web3.js";

interface Token {
  chainId: number;
  address: string;
  symbol: string;
  name: string;
  decimals: number;
  logoURI: string;
  tags: string[];
}

(async () => {
  const ENV = "devnet";
  const tokens: Token[] = await (await fetch(TOKEN_LIST_URL[ENV])).json();

  const USER_KEYPAIR = Keypair.generate();
  const connection = new Connection("https://api.devnet.solana.com");

  const jupiter = await Jupiter.load({
    connection,
    cluster: ENV,
    user: USER_KEYPAIR, 
  });

  const routeMap = jupiter.getRouteMap();
})();

指定された入力および出力トークンのルートを取得する

computeRoutesメソッドは、入力Mintアドレスと出力Mintアドレスを受け取り、すべての可能なルートを最安値の順に提供します。

Press </> button to view full source
import { Jupiter, RouteInfo, TOKEN_LIST_URL } from "@jup-ag/core";
import { Connection, PublicKey, Keypair } from "@solana/web3.js";

interface Token {
  chainId: number;
  address: string;
  symbol: string;
  name: string;
  decimals: number;
  logoURI: string;
  tags: string[];
}

(async () => {
  const ENV = "devnet";
  const tokens: Token[] = await (await fetch(TOKEN_LIST_URL[ENV])).json();

  const USER_KEYPAIR = Keypair.generate();
  const connection = new Connection("https://api.devnet.solana.com");

  const jupiter = await Jupiter.load({
    connection,
    cluster: ENV,
    user: USER_KEYPAIR, 
  });

  const routeMap = jupiter.getRouteMap();

  const inputToken = "So11111111111111111111111111111111111111112";
  const outputToken = "SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt";
  const inputAmount = 1; 
  const slippage = 1; 

  const routes = await jupiter.computeRoutes({
    inputMint: new PublicKey(inputToken), 
    outputMint: new PublicKey(outputToken), 
    inputAmount, 
    slippage, 
    forceFetch: false, 
  });
})();

トークンスワップを実行する

ここでexchangeメソッドが呼び出され、特定のルートのトランザクションが構築されます。

Press </> button to view full source
import { Jupiter, RouteInfo, TOKEN_LIST_URL } from "@jup-ag/core";
import { Connection, PublicKey, Keypair } from "@solana/web3.js";

interface Token {
  chainId: number;
  address: string;
  symbol: string;
  name: string;
  decimals: number;
  logoURI: string;
  tags: string[];
}

(async () => {
  const ENV = "devnet";
  const tokens: Token[] = await (await fetch(TOKEN_LIST_URL[ENV])).json();

  const USER_KEYPAIR = Keypair.generate();
  const connection = new Connection("https://api.devnet.solana.com");

  const jupiter = await Jupiter.load({
    connection,
    cluster: ENV,
    user: USER_KEYPAIR, 
  });

  const routeMap = jupiter.getRouteMap();

  const inputToken = "So11111111111111111111111111111111111111112";
  const outputToken = "SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt";
  const inputAmount = 1; 
  const slippage = 1; 

  const routes = await jupiter.computeRoutes({
    inputMint: new PublicKey(inputToken), 
    outputMint: new PublicKey(outputToken),
    inputAmount, 
    slippage,
    forceFetch: false, 
  });

  const { execute } = await jupiter.exchange({
    routeInfo: routes.routesInfos[0],
  });
  
  const swapResult: any = await execute(); 
  
})();

React アプリケーションで Jupiter を使用する方法

インストール

yarn add @jup-ag/react-hook
npm install @jup-ag/react-hook

プロバイダーの追加

React アプリ全体で useJupiter フックを使用するために、ここで JupiterProvider をセットアップしています。クラスター パラメーターは、さまざまなトークンを取得するためにmainnet-beta として設定されていますが、必要に応じてdevnetに変更することもできます

Press </> button to view full source
import {
  ConnectionProvider,
  WalletProvider,
  useConnection,
  useWallet,
} from "@solana/wallet-adapter-react";
import {
  getLedgerWallet,
  getPhantomWallet,
  getSlopeWallet,
  getSolflareWallet,
  getSolletExtensionWallet,
  getSolletWallet,
  getTorusWallet,
} from "@solana/wallet-adapter-wallets";

const JupiterApp = ({ children }) => {
  const { connection } = useConnection();
  const wallet = useWallet();

  return (
    <JupiterProvider
      cluster="mainnet-beta"
      connection={connection}
      userPublicKey={wallet.publicKey || undefined}
    >
      {children}
    </JupiterProvider>
  );
};

const App = ({ children }) => {
  const network = WalletAdapterNetwork.Devnet;
  const wallets = useMemo(
    () => [
      getPhantomWallet(),
      getSlopeWallet(),
      getSolflareWallet(),
      getTorusWallet(),
      getLedgerWallet(),
      getSolletWallet({ network }),
      getSolletExtensionWallet({ network }),
    ],
    [network]
  );
  const endpoint = "https://solana-api.projectserum.com";

  return (
    <ConnectionProvider endpoint={endpoint}>
      <WalletProvider wallets={wallets} autoConnect>
        <JupiterApp>{children}</JupiterApp>
      </WalletProvider>
    </ConnectionProvider>
  );
};

export default App;

トークンのリストを取得する

特定のネットワークでスワップできるすべての可能なトークンが取得され、状態に格納されます。

Press </> button to view full source
import { TOKEN_LIST_URL } from "@jup-ag/core";

const JupiterApp = () => {
  const [tokens, setTokens] = useState<Token[]>([]);
  useEffect(() => {
    fetch(TOKEN_LIST_URL[ENV])
      .then((response) => response.json())
      .then((result) => setTokens(result));
  }, []);
};

export default JupiterApp;

Stateの設定

InputMintとOutputMintは、互いに交換したり、ユーザーから取得したりできるようにするために追加されるStateです。

Press </> button to view full source
import { TOKEN_LIST_URL } from "@jup-ag/core";

const JupiterApp = () => {
  const [tokens, setTokens] = useState<Token[]>([]);
  const [inputMint] = useState<PublicKey>(
    new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v")
  );
  const [outputMint] = useState<PublicKey>(
    new PublicKey("Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB")
  );

  useEffect(() => {

    fetch(TOKEN_LIST_URL[ENV])
      .then((response) => response.json())
      .then((result) => setTokens(result));
  }, []);
};

export default JupiterApp;

useJupiter react hookの仕様

useJupiterフックは、必要なすべてのパラメーターを取得して、InputMint と OutputMintの両方のトークンを交換できるルートを見つけます。詳細についてはこちらopen in new window

Press </> button to view full source
import { TOKEN_LIST_URL } from "@jup-ag/core";

const JupiterApp = () => {
  const [tokens, setTokens] = useState<Token[]>([]);
  const [inputMint] = useState<PublicKey>(
    new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v")
  );
  const [outputMint] = useState<PublicKey>(
    new PublicKey("Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB")
  );

  useEffect(() => {

    fetch(TOKEN_LIST_URL[ENV])
      .then((response) => response.json())
      .then((result) => setTokens(result));
  }, []);

  const jupiter = useJupiter({
    amount: 1 * 10 ** 6,
    inputMint,
    outputMint,
    slippage: 1, 
    debounceTime: 250, 
  });

  const {
    allTokenMints, 
    routeMap, 
    exchange,
    refresh,
    lastRefreshTimestamp, 
    loading,
    routes,
    error,
  } = jupiter;

  return (
    <>
      <div style={{ fontWeight: "600", fontSize: 16, marginTop: 24 }}>
        Hook example
      </div>
      <div>Number of tokens: {tokens.length}</div>
      <div>Number of input tokens {allTokenMints.length}</div>
      <div>Possible number of routes: {routes?.length}</div>
      <div>Best quote: {routes ? routes[0].outAmount : ""}</div>
    </>
  );
};

export default JupiterApp;

スワップの実行

すべてのデータを useJupiter フックに提供した後exchangeメソッドを使用して、jupiterインスタンスを使用してスワップを実行できます。

Press </> button to view full source
import { TOKEN_LIST_URL } from "@jup-ag/core";

const JupiterApp = () => {
  const [tokens, setTokens] = useState<Token[]>([]);
  const [inputMint] = useState<PublicKey>(
    new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v")
  );
  const [outputMint] = useState<PublicKey>(
    new PublicKey("Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB")
  );

  useEffect(() => {

    fetch(TOKEN_LIST_URL[ENV])
      .then((response) => response.json())
      .then((result) => setTokens(result));
  }, []);

  const jupiter = useJupiter({
    amount: 1 * 10 ** 6, 
    inputMint,
    outputMint,
    slippage: 1, 
    debounceTime: 250, 
  });

  const {
    allTokenMints,
    routeMap,
    exchange, 
    refresh,
    lastRefreshTimestamp,
    loading, 
    routes, 
    error,
  } = jupiter;

  const onClickSwapBestRoute = async () => {

    const bestRoute = routes[0];

    await exchange({
      wallet: {
        sendTransaction: wallet.sendTransaction,
        publicKey: wallet.publicKey,
        signAllTransactions: wallet.signAllTransactions,
        signTransaction: wallet.signTransaction,
      },
      route: bestRoute,
      confirmationWaiterFactory: async (txid) => {
        console.log("sending transaction");
        await connection.confirmTransaction(txid);
        console.log("confirmed transaction");

        return await connection.getTransaction(txid, {
          commitment: "confirmed",
        });
      },
    });

    console.log({ swapResult });

    if ("error" in swapResult) {
      console.log("Error:", swapResult.error);
    } else if ("txid" in swapResult) {
      console.log("Sucess:", swapResult.txid);
      console.log("Input:", swapResult.inputAmount);
      console.log("Output:", swapResult.outputAmount);
    }
  };

  return (
    <>
      <div style={{ fontWeight: "600", fontSize: 16, marginTop: 24 }}>
        Hook example
      </div>
      <div>Number of tokens: {tokens.length}</div>
      <div>Number of input tokens {allTokenMints.length}</div>
      <div>Possible number of routes: {routes?.length}</div>
      <div>Best quote: {routes ? routes[0].outAmount : ""}</div>
      <button type="button" onClick={onClickSwapBestRoute}>
        Swap best route
      </button>
    </>
  );
};

export default JupiterApp;

Jupiter APIの使用方法

これは、 jupiter programとやり取りして、提供された2つのトークンを交換する最も簡単な方法です。

インストール

yarn i @solana/web3.js
yarn i cross-fetch
yarn i @project-serum/anchor
yarn i bs58
npm i @solana/web3.js
npm i cross-fetch
npm i @project-serum/anchor
npm i bs58

ルートマップの取得

この API は、jupiter API を使用してスワップできるすべての利用可能なトークンを取得します。すべての可能なトークン ルートのリストがここで取得されます。allInputMints には、ミント アドレスごとのすべての可能な入力トークンのリストが含まれ、swappableOutputForSol には、この場合にSOLにスワップできるすべての可能なトークンが含まれます。

Press </> button to view full source
const routeMap = await(
  await fetch("https://quote-api.jup.ag/v1/route-map")
).json();


const allInputMints = Object.keys(routeMap);


const swappableOutputForSol =
  routeMap["So11111111111111111111111111111111111111112"];

シリアル化されたトランザクションを取得してスワップを実行する

POST APIリクエストには、利用したいルートとユーザーのウォレットアドレスを指定します。このAPIには、wrapUnwrapSOLfeeAccountなどのオプションパラメータを追加することができます。 feeAccountについて詳しくは、こちらopen in new windowの公式ドキュメントをご覧ください。

Press </> button to view full source
(async() => {
  const transactions = await(
     fetch("https://quote-api.jup.ag/v1/swap", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
     
        route: routes[0],
  
        userPublicKey: wallet.publicKey.toString(),
      
        wrapUnwrapSOL: true,
  
        feeAccount: "xxxx",
      }),
    })
  ).json();
  
  const { setupTransaction, swapTransaction, cleanupTransaction } = transactions;
})()

スワップトランザクションの実行

Transactionオブジェクトが作成され、ユーザーによって署名されます。

Press </> button to view full source

(async() => {
  for (let serializedTransaction of [
    setupTransaction,
    swapTransaction,
    cleanupTransaction,
  ].filter(Boolean)) {
   
    const transaction = Transaction.from(
      Buffer.from(serializedTransaction, "base64")
    );
  
    const txid = await connection.sendTransaction(transaction, [wallet.payer], {
      skipPreflight: false,
    });
    await connection.confirmTransaction(txid);
  
  }  
})()

その他参考資料

Last Updated:
Contributors: PokoPoko2ry