forked from anza-xyz/octane
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Command-line tools to manage fee payer accounts and helper functions …
…in payer-utils (anza-xyz#16) * add CLI * Refactor CLI and decouple fee payer utils to payer-utils in core * restore original config * add cli command to generate config for one token * fix swap-tokens-to-sol cli command description * use TokenFee in new endpoints as well
- Loading branch information
1 parent
2dbbe9c
commit f06b842
Showing
24 changed files
with
964 additions
and
75 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import { PublicKey } from '@solana/web3.js'; | ||
|
||
type SerializableTokenFee = { | ||
mint: string; | ||
account: string; | ||
decimals: number; | ||
fee: number; | ||
} | ||
|
||
export class TokenFee { | ||
public mint: PublicKey; | ||
public account: PublicKey; | ||
public decimals: number; | ||
public fee: bigint; | ||
|
||
constructor(mint: PublicKey, account: PublicKey, decimals: number, fee: bigint) { | ||
this.mint = mint; | ||
this.account = account; | ||
this.decimals = decimals; | ||
this.fee = fee; | ||
} | ||
|
||
toSerializable(): SerializableTokenFee { | ||
return { | ||
mint: this.mint.toBase58(), | ||
account: this.account.toBase58(), | ||
decimals: this.decimals, | ||
fee: Number(this.fee) | ||
}; | ||
} | ||
|
||
static fromSerializable(serializableToken: SerializableTokenFee): TokenFee { | ||
return new TokenFee( | ||
new PublicKey(serializableToken.mint), | ||
new PublicKey(serializableToken.account), | ||
serializableToken.decimals, | ||
BigInt(serializableToken.fee) | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
export * from './actions'; | ||
export * as core from './core'; | ||
export * as PayerUtils from './payer-utils'; | ||
export * from './swapProviders'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import { Connection, Keypair, PublicKey } from '@solana/web3.js'; | ||
import { TokenFee } from '../core'; | ||
import { createAssociatedTokenAccount, getAssociatedTokenAddress } from '@solana/spl-token'; | ||
|
||
export type CreateAccount = { | ||
address: PublicKey; | ||
mint: PublicKey; | ||
}; | ||
|
||
export type CreateAccountResult = { | ||
address: PublicKey; | ||
mint: PublicKey; | ||
error: Error | null; | ||
}; | ||
|
||
export async function buildCreateAccountListFromTokenFees( | ||
connection: Connection, | ||
feePayer: PublicKey, | ||
tokenFees: TokenFee[] | ||
): Promise<CreateAccount[]> { | ||
let createAccounts: CreateAccount[] = []; | ||
for (const tokenFee of tokenFees) { | ||
const alreadyCreated = await connection.getAccountInfo(tokenFee.account); | ||
if (alreadyCreated) { | ||
continue; | ||
} | ||
|
||
const associatedWithFeePayer = tokenFee.account.equals( | ||
await getAssociatedTokenAddress(tokenFee.mint, feePayer) | ||
); | ||
if (!associatedWithFeePayer) { | ||
continue; | ||
} | ||
|
||
createAccounts.push({ mint: tokenFee.mint, address: tokenFee.account }); | ||
} | ||
|
||
return createAccounts; | ||
} | ||
|
||
export async function createAccounts( | ||
connection: Connection, | ||
feePayer: Keypair, | ||
accounts: CreateAccount[] | ||
): Promise<CreateAccountResult[]> { | ||
let results: CreateAccountResult[] = []; | ||
|
||
for (const account of accounts) { | ||
let error: Error | null = null; | ||
try { | ||
await createAssociatedTokenAccount( | ||
connection, | ||
feePayer, | ||
account.mint, | ||
feePayer.publicKey, | ||
); | ||
} catch (e) { | ||
error = e as Error; | ||
} | ||
|
||
results.push({...account, error}) | ||
} | ||
|
||
return results; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export * from './accounts'; | ||
export * from './tokenFees'; | ||
export * from './jupiter'; | ||
export * from './swaps'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
import axios from 'axios'; | ||
import { PublicKey, Transaction } from '@solana/web3.js'; | ||
import { NATIVE_MINT } from '@solana/spl-token'; | ||
|
||
export type TokenPriceInfo = { | ||
id: string; | ||
mintSymbol: string; | ||
vsToken: string; | ||
vsTokenSymbol: string; | ||
price: number; | ||
} | ||
|
||
type TokenPriceInfoResponse = { | ||
data: TokenPriceInfo; | ||
timeTaken: number; | ||
} | ||
|
||
export type Route = { | ||
inAmount: number; | ||
outAmount: number; | ||
amount: number; | ||
otherAmountThreshold: number; | ||
outAmountWithSlippage: number; | ||
swapMode: string; | ||
priceImpactPct: number; | ||
marketInfos: RouteMarketInfo[]; | ||
} | ||
|
||
export type RouteMarketInfo = { | ||
id: string; | ||
label: string; | ||
inputMint: string; | ||
outputMint: string; | ||
inAmount: number; | ||
outAmount: number; | ||
lpFee: RouteFee; | ||
platformFee: RouteFee; | ||
notEnoughLiquidity: boolean; | ||
priceImpactPct: number; | ||
minInAmount?: number; | ||
minOutAmount?: number; | ||
} | ||
|
||
export type RouteFee = { | ||
amount: number; | ||
mint: string; | ||
pct: number; | ||
} | ||
|
||
type RoutesResponse = { | ||
data: Route[]; | ||
timeTaken: number; | ||
contextSlot: string; | ||
} | ||
|
||
export type SwapTransactions = { | ||
setup: Transaction | null; | ||
swap: Transaction | null; | ||
cleanup: Transaction | null; | ||
} | ||
|
||
type SwapTransactionsResponse = { | ||
setupTransaction: string | null; | ||
swapTransaction: string | null; | ||
cleanupTransaction: string | null; | ||
} | ||
|
||
export async function getPopularTokens(count: number, excludeNative = true): Promise<PublicKey[]> { | ||
const response = await axios.get('https://cache.jup.ag/top-tokens'); | ||
const mints = response.data.map((mint: string) => new PublicKey(mint)) as PublicKey[]; | ||
const filteredMints = excludeNative ? mints.filter(value => !value.equals(NATIVE_MINT)) : mints; | ||
return filteredMints.slice(0, count); | ||
} | ||
|
||
export async function getTokenToNativePriceInfo(mint: PublicKey): Promise<TokenPriceInfo> { | ||
const priceInfoResponse = ( | ||
await axios.get('https://price.jup.ag/v1/price', {params: {id: 'SOL', vsToken: mint.toBase58()}}) | ||
).data as TokenPriceInfoResponse; | ||
return priceInfoResponse.data; | ||
} | ||
|
||
export async function getRoutes( | ||
inputMint: PublicKey, | ||
outputMint: PublicKey, | ||
amount: BigInt, | ||
slippage: number | ||
): Promise<Route[]> { | ||
const params = { | ||
inputMint: inputMint.toBase58(), | ||
outputMint: outputMint.toBase58(), | ||
amount: amount, | ||
slippage: slippage, | ||
}; | ||
const routesResponse = (await axios.get( | ||
'https://quote-api.jup.ag/v1/quote', { params } | ||
)).data as RoutesResponse; | ||
return routesResponse.data; | ||
} | ||
|
||
export async function getSwapTransactions(wallet: PublicKey, route: Route): Promise<SwapTransactions> { | ||
const decodeTransactionOrNull = (serialized: string | null) => ( | ||
serialized !== null ? Transaction.from(Buffer.from(serialized, 'base64')) : null | ||
); | ||
|
||
const response = ( | ||
await axios.post('https://quote-api.jup.ag/v1/swap', { | ||
route, | ||
userPublicKey: wallet.toString(), | ||
wrapUnwrapSOL: true, | ||
}, { | ||
headers: { 'Content-Type': 'application/json' } | ||
}) | ||
).data as SwapTransactionsResponse; | ||
return { | ||
setup: decodeTransactionOrNull(response.setupTransaction), | ||
swap: decodeTransactionOrNull(response.swapTransaction), | ||
cleanup: decodeTransactionOrNull(response.cleanupTransaction), | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import { Connection, Keypair } from '@solana/web3.js'; | ||
import { getAccount, NATIVE_MINT } from '@solana/spl-token'; | ||
import { TokenFee } from '../core'; | ||
import { getRoutes, getSwapTransactions, Route } from './jupiter'; | ||
|
||
export async function loadSwapRoutesForTokenFees( | ||
connection: Connection, | ||
tokenFees: TokenFee[], | ||
thresholdInLamports: number, | ||
slippage: number = 0.5 | ||
): Promise<Route[]> { | ||
let routes = []; | ||
for (const tokenFee of tokenFees) { | ||
const account = await getAccount(connection, tokenFee.account); | ||
if (account.amount === 0n) { | ||
continue; | ||
} | ||
const route = (await getRoutes( | ||
tokenFee.mint, NATIVE_MINT, account.amount, slippage | ||
))[0]; | ||
if (route.outAmount < thresholdInLamports) { | ||
continue; | ||
} | ||
routes.push(route); | ||
} | ||
return routes; | ||
} | ||
|
||
export async function executeSwapByRoute(connection: Connection, feePayer: Keypair, route: Route): Promise<string[]> { | ||
const transactions = await getSwapTransactions(feePayer.publicKey, route); | ||
let txids = []; | ||
for (const transaction of [transactions.setup, transactions.swap, transactions.cleanup]) { | ||
if (transaction === null) { | ||
continue; | ||
} | ||
const txid = await connection.sendTransaction( | ||
transaction, | ||
[feePayer], | ||
{ skipPreflight: true } | ||
); | ||
await connection.confirmTransaction(txid); | ||
txids.push(txid); | ||
} | ||
return txids; | ||
} |
Oops, something went wrong.