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 :
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();
})();
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 [ici].(https://docs.jup.ag/jupiter-core/full-guide)
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 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.
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 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.
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 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é.
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();
})();
bestRoute = routes.routesInfos[0];
const { execute } = await jupiter.exchange({
bestRoute,
});
const swapResult = 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.
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;
const JupiterApp = ({ children }) => {
const { connection } = useConnection();
const wallet = useWallet();
return (
<JupiterProvider
cluster="mainnet-beta"
connection={connection}
userPublicKey={wallet.publicKey || undefined}
>
{children}
</JupiterProvider>
);
};
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.
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;
const [tokens, setTokens] = useState<Token[]>([]);
useEffect(() => {
fetch(TOKEN_LIST_URL[ENV])
.then((response) => response.json())
.then((result) => setTokens(result));
}, []);
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.
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;
const [inputMint] = useState<PublicKey>(
new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v")
);
const [outputMint] = useState<PublicKey>(
new PublicKey("Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB")
);
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 ici
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;
const jupiter = useJupiter({
amount: 1 * 10 ** 6,
inputMint,
outputMint,
slippage: 1,
debounceTime: 250,
});
const {
allTokenMints,
routeMap,
exchange,
refresh,
lastRefreshTimestamp,
loading,
routes,
error,
} = jupiter;
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
.
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;
(async() => {
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",
});
},
});
})()
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.
const routeMap = await(
await fetch("https://quote-api.jup.ag/v1/route-map")
).json();
const allInputMints = Object.keys(routeMap);
const swappableOutputForSol =
routeMap["So11111111111111111111111111111111111111112"];
const routeMap = await(
await fetch("https://quote-api.jup.ag/v1/route-map")
).json();
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 ici
(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;
})()
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",
}),
});
Exécution de l'Opération d'Echange
Un objet Transaction est créé puis signé par l'utilisateur.
(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);
}
})()
const transaction = Transaction.from(
Buffer.from(serializedTransaction, "base64")
);
const txid = await connection.sendTransaction(transaction, [wallet.payer], {
skipPreflight: false,
});