Skip to content

Commit

Permalink
api: add jupiter swap
Browse files Browse the repository at this point in the history
  • Loading branch information
0x0ece committed May 28, 2024
1 parent d3a5864 commit 254ebab
Show file tree
Hide file tree
Showing 11 changed files with 234 additions and 192 deletions.
13 changes: 3 additions & 10 deletions anchor/Anchor.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,28 @@ seeds = false
skip-lint = false

[programs.devnet]
custody = "Gcu1vbed9bwpfwU9PCnJw8QanQfVHfETWxAK3EZczbgo"
glam = "Gco1pcjxCMYjKJjSNJ7mKV7qezeUTE7arXJgy7PAPNRc"
policy = "Gpo1jXtEFepqyPQWTG7oDJrg8rye8JL3zcsczHzXKqLt"
pricing = "Gpr1WZZXAty2L9eiMwZPC7ra69vMFojhdqDuiRHkQQQp"
strategy = "Gst1YGe5vKURSWfCqM7GhZys1oML9E9t91WRyV2rt4yS"

[programs.localnet]
custody = "Gcu1vbed9bwpfwU9PCnJw8QanQfVHfETWxAK3EZczbgo"
glam = "Gco1pcjxCMYjKJjSNJ7mKV7qezeUTE7arXJgy7PAPNRc"
policy = "Gpo1jXtEFepqyPQWTG7oDJrg8rye8JL3zcsczHzXKqLt"
pricing = "Gpr1WZZXAty2L9eiMwZPC7ra69vMFojhdqDuiRHkQQQp"
strategy = "Gst1YGe5vKURSWfCqM7GhZys1oML9E9t91WRyV2rt4yS"

[registry]
url = "https://api.apr.dev"

[provider]
cluster = "localnet"
#cluster = "devnet"
#cluster = "mainnet"
wallet = "~/.config/solana/id.json"

[scripts]
test = "../node_modules/.bin/nx run --skip-nx-cache anchor:jest --verbose --testPathPattern tests/ --testNamePattern glam_crud"
#test = "../node_modules/.bin/nx run --skip-nx-cache anchor:jest --verbose --testPathPattern tests/ --testNamePattern glam_crud"
#test = "../node_modules/.bin/nx run --skip-nx-cache anchor:jest --verbose --testPathPattern tests/ --testNamePattern glam_investor"
#test = "../node_modules/.bin/nx run --skip-nx-cache anchor:jest --verbose --testPathPattern tests/ --testNamePattern glam_drift"
#test = "../node_modules/.bin/nx run --skip-nx-cache anchor:jest --verbose --testPathPattern tests/ --testNamePattern glam_staking"
#test = "../node_modules/.bin/nx run --skip-nx-cache anchor:jest --verbose --testPathPattern tests/ --testNamePattern glam_jupiter"
#test = "../node_modules/.bin/nx run --skip-nx-cache anchor:jest --verbose --testPathPattern tests/ --testNamePattern glam_openfunds"
#test = "../node_modules/.bin/nx run --skip-nx-cache anchor:jest --verbose --testPathPattern tests/ --testNamePattern glam_wsol"
test = "../node_modules/.bin/nx run --skip-nx-cache anchor:jest --verbose --testPathPattern tests/ --testNamePattern glam_wsol"
#test = "../node_modules/.bin/nx run --skip-nx-cache anchor:jest --verbose --testPathPattern tests/ --testNamePattern devnet"

[test]
Expand Down
8 changes: 4 additions & 4 deletions anchor/programs/glam/src/instructions/jupiter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@ impl anchor_lang::Id for Jupiter {

#[derive(Accounts)]
pub struct JupiterSwap<'info> {
#[account(mut)]
pub manager: Signer<'info>,
#[account(has_one = manager, has_one = treasury)]
pub fund: Box<Account<'info, FundAccount>>,

#[account(mut, seeds = [b"treasury".as_ref(), fund.key().as_ref()], bump)]
pub treasury: SystemAccount<'info>,

#[account(has_one = manager, has_one = treasury)]
pub fund: Box<Account<'info, FundAccount>>,
#[account(mut)]
pub manager: Signer<'info>,

pub jupiter_program: Program<'info, Jupiter>,
pub system_program: Program<'info, System>,
Expand Down
12 changes: 11 additions & 1 deletion anchor/src/client/base.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import * as anchor from "@coral-xyz/anchor";
import { Program, IdlAccounts } from "@coral-xyz/anchor";
import {
AnchorProvider,
IdlAccounts,
Program,
Wallet
} from "@coral-xyz/anchor";
import {
ComputeBudgetProgram,
Connection,
PublicKey,
Signer,
TransactionSignature
} from "@solana/web3.js";
import {
Expand Down Expand Up @@ -54,6 +60,10 @@ export class BaseClient {
return this.provider?.publicKey || new PublicKey(0);
}

getWalletSigner(): Signer {
return ((this.provider as AnchorProvider).wallet as Wallet).payer;
}

getFundModel(fund: any): FundModel {
return new FundModel(fund) as FundModel;
}
Expand Down
240 changes: 122 additions & 118 deletions anchor/src/client/jupiter.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import * as anchor from "@coral-xyz/anchor";
import { BN } from "@coral-xyz/anchor";
import {
PublicKey,
Transaction,
TransactionInstruction,
TransactionSignature
TransactionSignature,
TransactionMessage,
VersionedTransaction,
sendAndConfirmTransaction
} from "@solana/web3.js";

import { BaseClient } from "./base";
Expand All @@ -21,113 +22,126 @@ export class JupiterClient {
* Client methods
*/

getQuote = async (
fromMint: PublicKey,
toMint: PublicKey,
amount: number,
slippage: number,
onlyDirectRoutes: boolean
) => {
const reponse = await fetch(
`${JUPITER_API}/quote?outputMint=${toMint.toBase58()}&inputMint=${fromMint.toBase58()}&amount=${amount}&slippage=${slippage}&onlyDirectRoutes=${onlyDirectRoutes}`
);
return await reponse.json();
};

getSwapIx = async (
fromAccount: PublicKey,
toAccount: PublicKey,
quote: any
): Promise<any> => {
const data = {
quoteResponse: quote,
userPublicKey: fromAccount.toBase58(),
destinationTokenAccount: toAccount.toBase58()
};
const response = await fetch(`${JUPITER_API}/swap-instructions`, {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify(data)
});

const resp = await response.json();
console.log("swap-instructions response:", resp);
return resp;
};

public async swap(
fund: PublicKey,
fromMint: PublicKey,
toMint: PublicKey,
amount: BN,
slippage: number = 0.5,
onlyDirectRoutes: boolean = true
quote?: any,
quoteResponse?: any,
swapInstruction?: any,
addressLookupTableAddresses?: any
): Promise<TransactionSignature> {
const fromAccount = this.base.getTreasuryAta(fund, fromMint);
const toAccount = this.base.getTreasuryAta(fund, toMint);

const payload = await this.jupiterPayload(
fromMint,
toMint,
fromAccount,
toAccount,
amount,
slippage,
onlyDirectRoutes
const tx = await this.swapTxBuilder(
fund,
this.base.getManager(),
quote,
quoteResponse,
swapInstruction,
addressLookupTableAddresses
);

return this.swapTxBuilder(fund, this.base.getManager(), payload.data).rpc();
tx.sign([this.base.getWalletSigner()]);
return await this.base.provider.connection.sendTransaction(tx);
}

/*
* Tx Builders
*/

swapTxBuilder(fund: PublicKey, manager: PublicKey, data: Buffer): any {
return this.base.program.methods.jupiterSwap(data).accounts({
fund,
manager,
treasury: this.base.getTreasuryPDA(fund),
jupiterProgram
});
async swapTxBuilder(
fund: PublicKey,
manager: PublicKey,
quote?: any,
quoteResponse?: any,
swapInstruction?: any,
addressLookupTableAddresses?: any
): Promise<VersionedTransaction> /* MethodsBuilder<Glam, ?> */ {
if (swapInstruction === undefined) {
if (quoteResponse === undefined) {
/* Fetch quoteResponse is not specified */
console.log("Fetching quoteResponse...");
quoteResponse = await this.getQuoteResponse(quote);
}
/* Fetch swapInstruction is not specified */
console.log("Fetching swapInstruction...");
[swapInstruction, addressLookupTableAddresses] =
await this.getSwapInstruction({
userPublicKey: manager,
quoteResponse
});
}
// console.log("swapInstruction", swapInstruction);
/* Create the tx for jupiterSwap using the swapInstruction */
const tx = await this.base.program.methods
.jupiterSwap(swapInstruction.data)
.accounts({
fund,
manager,
treasury: this.base.getTreasuryPDA(fund),
jupiterProgram
})
.remainingAccounts(swapInstruction.keys)
.transaction();

const connection = this.base.provider.connection;
const lookupTableAccounts = (
await Promise.all(
addressLookupTableAddresses.map(
async (address) =>
(
await connection.getAddressLookupTable(new PublicKey(address))
).value
)
)
).filter((x) => !!x);

const messageV0 = new TransactionMessage({
payerKey: manager,
recentBlockhash: (await connection.getLatestBlockhash()).blockhash,
instructions: tx.instructions
}).compileToV0Message(lookupTableAccounts);

return new VersionedTransaction(messageV0);
}

async jupiterPayload(
fromMint: PublicKey,
toMint: PublicKey,
fromAccount: PublicKey,
toAccount: PublicKey,
amount: BN,
slippage: number,
onlyDirectRoutes: boolean
): Promise<TransactionInstruction> {
const quote = await this.getQuote(
fromMint,
toMint,
amount,
slippage,
onlyDirectRoutes
);
console.log("quote response:", quote);

const { swapInstruction } = await this.getSwapIx(
fromAccount,
toAccount,
quote
public async getQuoteResponse(quote: any): Promise<any> {
const res = await fetch(
`${JUPITER_API}/quote?` + new URLSearchParams(Object.entries(quote))
);
const quoteResponse = await res.json();
// console.log("quoteResponse", quoteResponse);
return quoteResponse;
}

return new TransactionInstruction({
programId: new PublicKey(swapInstruction.programId),
keys: swapInstruction.accounts.map((key: any) => ({
pubkey: new PublicKey(key.pubkey),
isSigner: key.isSigner,
isWritable: key.isWritable
})),
data: Buffer.from(swapInstruction.data, "base64")
public async getSwapInstructions(quoteResponse: any): Promise<any> {
const res = await fetch(`${JUPITER_API}/swap-instructions`, {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify(quoteResponse)
});

const instructions = await res.json();
return instructions;
}

async getSwapInstruction(
quoteResponse
): Promise<[TransactionInstruction, string[]]> {
const { swapInstruction, addressLookupTableAddresses } =
await this.getSwapInstructions(quoteResponse);

return [
new TransactionInstruction({
programId: new PublicKey(swapInstruction.programId),
keys: swapInstruction.accounts.map((key: any) => ({
pubkey: new PublicKey(key.pubkey),
isSigner: key.isSigner,
isWritable: key.isWritable
})),
data: Buffer.from(swapInstruction.data, "base64")
}),
addressLookupTableAddresses
];
}

/*
Expand All @@ -136,29 +150,19 @@ export class JupiterClient {

public async swapTx(
fund: PublicKey,
fromMint: PublicKey,
toMint: PublicKey,
amount: BN,
slippage: number = 0.5,
onlyDirectRoutes: boolean = true
): Promise<Transaction> {
const fromAccount = this.base.getTreasuryAta(fund, fromMint);
const toAccount = this.base.getTreasuryAta(fund, toMint);

const payload = await this.jupiterPayload(
fromMint,
toMint,
fromAccount,
toAccount,
amount,
slippage,
onlyDirectRoutes
);

return this.swapTxBuilder(
manager: PublicKey,
quote?: any,
quoteResponse?: any,
swapInstruction?: any,
addressLookupTableAddresses?: any
): Promise<VersionedTransaction> {
return await this.swapTxBuilder(
fund,
this.base.getManager(),
payload.data
).transaction();
manager,
quote,
quoteResponse,
swapInstruction,
addressLookupTableAddresses
);
}
}
12 changes: 6 additions & 6 deletions anchor/target/idl/glam.json
Original file line number Diff line number Diff line change
Expand Up @@ -825,19 +825,19 @@
"name": "jupiterSwap",
"accounts": [
{
"name": "manager",
"isMut": true,
"isSigner": true
"name": "fund",
"isMut": false,
"isSigner": false
},
{
"name": "treasury",
"isMut": true,
"isSigner": false
},
{
"name": "fund",
"isMut": false,
"isSigner": false
"name": "manager",
"isMut": true,
"isSigner": true
},
{
"name": "jupiterProgram",
Expand Down
Loading

0 comments on commit 254ebab

Please sign in to comment.