Jupiter

Jupiter est le principal agrégateur de liquidités pour Solana, offrant la plus large gamme de jetons et la meilleure recherche de route entre n'importe quelle paire de jetons.

Installation

@jup-ag/core est le paquet de base (Core package) utilisé pour interagir avec les programmes on-chain de jupiter afin d'effectuer des échanges entre deux paires de jetons possibles.

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

Récupération de la liste des jetons depuis Jupiter

Tous les jetons possibles qui peuvent être échangés avec Jupiter pour un réseau donné sont récupérés comme cela :

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();
})();

Chargement de l'instance Jupiter

L'instance de Jupiter est créée avec les configurations fournies. Il existe de nombreux paramètres optionnels que l'instance peut prendre, pour en savoir plus allez iciopen 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, 
  });
})();

Obtenir le Chemin d'Accès (RouteMap)

La RouteMap identifie les jetons qui peuvent être échangés pour un jeton d'entrée donné. Le chemin d'accès ne contient que les adresses de mint des jetons et aucune métadonnée.

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();
})();

Obtention des chemins pour un jeton d'Entrée et de Sortie donné

La méthode computeRoutes prend l'adresse de Mint d'entrée et l'adresse de Mint de sortie en argument et retourne tous les chemins possibles par ordre décroissant de meilleur prix.

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, 
  });
})();

Exécuter l'Echange de Jetons

La méthode exchange est appelée ici, elle construit la transaction pour un chemin donné.

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(); 
  
})();

Comment utiliser Jupiter dans une application React

Installation

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

Ajout du Provider

Nous configurons ici le JupiterProvider afin d'utiliser le Hook useJupiter dans l'application React. Le paramètre cluster est défini comme mainnet-beta afin d'obtenir une grande variété de jetons, mais si vous le souhaitez, vous pouvez également le changer en 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;

Récupération de la Liste de Jetons

Tous les jetons qu'il est possible d'échanger sur un réseau donné sont récupérés et stockés dans l'état.

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;

Création de l'État

InputMint et OutputMint sont des états qui sont ajoutés afin de pouvoir être échangés entre eux ou qui peuvent également être prélevés à l'utilisateur.

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;

Utilisation du hook react useJupiter

Le hook useJupiter prend tous les paramètres nécessaires pour trouver les chemins par lesquels les tokens renseignés dans InputMint et OutputMint peuvent être échangés. Pour en savoir plus, rendez-vous iciopen 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;

Exécution de l'Echange

Après avoir fourni toutes les données au hook useJupiter, il est possible d'utiliser l'instance jupiter pour effectuer un échange en utilisant la méthode exchange.

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;

Comment utiliser l'API de Jupiter

C'est le moyen le plus simple d'interagir avec les programmes de jupiter pour échanger deux jetons donnés.

Installation

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

Obtention du Chemin d'Accès

Cette API récupère tous les jetons disponibles qui peuvent être échangés en utilisant l'API jupiter. Une liste de tous les chemins possibles est récupérée ici et allInputMints contient la liste des adresses de mint de tous les jetons d'entrée possibles et swappableOutputForSol contient tous les jetons qu'il est possible d'échanger contre des 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"];

Obtention de la Transaction Sérialisée pour effectuer le Swap

La requête API POST est effectuée avec le chemin que nous souhaitons emprunter et l'adresse du portefeuille de l'utilisateur. Il y a quelques paramètres optionnels qui peuvent être ajoutés à cette api comme wrapUnwrapSOL et feeAccount. pour en savoir plus, consultez les documents officiels iciopen 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;
})()

Exécution de l'Opération d'Echange

Un objet Transaction est créé puis signé par l'utilisateur.

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);
  
  }  
})()

Autres Ressources

Last Updated: