Compare commits
2 Commits
0a528077e7
...
6437cdb701
Author | SHA1 | Date |
---|---|---|
Maximilian Schneider | 6437cdb701 | |
Maximilian Schneider | 671ad88c11 |
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@blockworks-foundation/mango-client",
|
||||
"version": "0.1.19",
|
||||
"version": "0.2.0",
|
||||
"description": "Library for interacting with Mango Markets' solana smart contracts.",
|
||||
"repository": "blockworks-foundation/mango-client-ts",
|
||||
"author": {
|
||||
|
|
16
src/ids.json
16
src/ids.json
|
@ -204,8 +204,8 @@
|
|||
"Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB"
|
||||
],
|
||||
"oracle_pks": [
|
||||
"HWh11EWkVHHZoRV6D6WzfRSna4yFv8ZvwcqDk74oDnSs",
|
||||
"AcYcDG74nxeFHxuqeD5RRWTMWKi77QVx7t9bEy8Y4eyN"
|
||||
"GVXRSBjFk6e6J3NbVPXohDJetcTjaeeuykUpbQF8UoMU",
|
||||
"JBu1AL4obBcCMqKBBxhpWCNUt136ijcuMZLFvTP7iWdB"
|
||||
],
|
||||
"spot_market_pks": [
|
||||
"C1EuT9VokAKLiW7i2ASnZUvxDoKuKkCpDDeNxAptuNe4",
|
||||
|
@ -235,8 +235,8 @@
|
|||
"BQcdHdAQW1hczDbBi9hiegXAR7A98Q9jx3X3iBBBDiq4"
|
||||
],
|
||||
"oracle_pks": [
|
||||
"HWh11EWkVHHZoRV6D6WzfRSna4yFv8ZvwcqDk74oDnSs",
|
||||
"AcYcDG74nxeFHxuqeD5RRWTMWKi77QVx7t9bEy8Y4eyN"
|
||||
"GVXRSBjFk6e6J3NbVPXohDJetcTjaeeuykUpbQF8UoMU",
|
||||
"JBu1AL4obBcCMqKBBxhpWCNUt136ijcuMZLFvTP7iWdB"
|
||||
],
|
||||
"spot_market_pks": [
|
||||
"5r8FfnbNYcQbS1m4CYmoHYGjBtu6bxfo6UJHNRfzPiYH",
|
||||
|
@ -292,10 +292,10 @@
|
|||
},
|
||||
"mango_program_id": "JD3bq9hGdy38PuWQ4h2YJpELmHVGPPfFSuFkpzAd9zfu",
|
||||
"oracles": {
|
||||
"BTC/USDT": "HWh11EWkVHHZoRV6D6WzfRSna4yFv8ZvwcqDk74oDnSs",
|
||||
"BTC/WUSDT": "HWh11EWkVHHZoRV6D6WzfRSna4yFv8ZvwcqDk74oDnSs",
|
||||
"ETH/USDT": "AcYcDG74nxeFHxuqeD5RRWTMWKi77QVx7t9bEy8Y4eyN",
|
||||
"ETH/WUSDT": "AcYcDG74nxeFHxuqeD5RRWTMWKi77QVx7t9bEy8Y4eyN",
|
||||
"BTC/USDT": "GVXRSBjFk6e6J3NbVPXohDJetcTjaeeuykUpbQF8UoMU",
|
||||
"BTC/WUSDT": "GVXRSBjFk6e6J3NbVPXohDJetcTjaeeuykUpbQF8UoMU",
|
||||
"ETH/USDT": "JBu1AL4obBcCMqKBBxhpWCNUt136ijcuMZLFvTP7iWdB",
|
||||
"ETH/WUSDT": "JBu1AL4obBcCMqKBBxhpWCNUt136ijcuMZLFvTP7iWdB",
|
||||
"SOL/USDT": "AyGcAbUyuCSMi3Mt5e3fqauajneDbFXgiA28FSbXpQer",
|
||||
"SRM/USDT": "76FmEELjApbgiyPjrNGBsUpWPDyNGq21LzmgUoSuVETC"
|
||||
},
|
||||
|
|
160
src/utils.ts
160
src/utils.ts
|
@ -1,9 +1,14 @@
|
|||
import {
|
||||
Account,
|
||||
AccountInfo, Commitment,
|
||||
AccountInfo,
|
||||
Commitment,
|
||||
Connection,
|
||||
PublicKey, RpcResponseAndContext, SimulatedTransactionResponse,
|
||||
SystemProgram, Transaction, TransactionConfirmationStatus,
|
||||
PublicKey,
|
||||
RpcResponseAndContext,
|
||||
SimulatedTransactionResponse,
|
||||
SystemProgram,
|
||||
Transaction,
|
||||
TransactionConfirmationStatus,
|
||||
TransactionInstruction,
|
||||
TransactionSignature,
|
||||
} from '@solana/web3.js';
|
||||
|
@ -21,7 +26,7 @@ import {
|
|||
zeros,
|
||||
} from '@project-serum/serum/lib/layout';
|
||||
|
||||
export const zeroKey = new PublicKey(new Uint8Array(32))
|
||||
export const zeroKey = new PublicKey(new Uint8Array(32));
|
||||
|
||||
export async function sleep(ms) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
|
@ -57,16 +62,16 @@ export async function awaitTransactionSignatureConfirmation(
|
|||
txid: TransactionSignature,
|
||||
timeout: number,
|
||||
connection: Connection,
|
||||
confirmLevel: TransactionConfirmationStatus
|
||||
confirmLevel: TransactionConfirmationStatus,
|
||||
) {
|
||||
let done = false;
|
||||
|
||||
const confirmLevels: (TransactionConfirmationStatus | null)[] = ['finalized']
|
||||
const confirmLevels: (TransactionConfirmationStatus | null)[] = ['finalized'];
|
||||
if (confirmLevel === 'confirmed') {
|
||||
confirmLevels.push('confirmed')
|
||||
confirmLevels.push('confirmed');
|
||||
} else if (confirmLevel === 'processed') {
|
||||
confirmLevels.push('confirmed')
|
||||
confirmLevels.push('processed')
|
||||
confirmLevels.push('confirmed');
|
||||
confirmLevels.push('processed');
|
||||
}
|
||||
|
||||
const result = await new Promise((resolve, reject) => {
|
||||
|
@ -113,7 +118,12 @@ export async function awaitTransactionSignatureConfirmation(
|
|||
console.log('REST error for', txid, result);
|
||||
done = true;
|
||||
reject(result.err);
|
||||
} else if (!(result.confirmations || confirmLevels.includes(result.confirmationStatus))) {
|
||||
} else if (
|
||||
!(
|
||||
result.confirmations ||
|
||||
confirmLevels.includes(result.confirmationStatus!)
|
||||
)
|
||||
) {
|
||||
console.log('REST not confirmed', txid, result);
|
||||
} else {
|
||||
console.log('REST confirmed', txid, result);
|
||||
|
@ -135,27 +145,27 @@ export async function awaitTransactionSignatureConfirmation(
|
|||
return result;
|
||||
}
|
||||
|
||||
|
||||
export async function createAccountInstruction(
|
||||
connection: Connection,
|
||||
payer: PublicKey,
|
||||
space: number,
|
||||
owner: PublicKey,
|
||||
lamports?: number
|
||||
): Promise<{ account: Account, instruction: TransactionInstruction }> {
|
||||
lamports?: number,
|
||||
): Promise<{ account: Account; instruction: TransactionInstruction }> {
|
||||
const account = new Account();
|
||||
const instruction = SystemProgram.createAccount({
|
||||
fromPubkey: payer,
|
||||
newAccountPubkey: account.publicKey,
|
||||
lamports: lamports ? lamports : await connection.getMinimumBalanceForRentExemption(space),
|
||||
lamports: lamports
|
||||
? lamports
|
||||
: await connection.getMinimumBalanceForRentExemption(space),
|
||||
space,
|
||||
programId: owner
|
||||
})
|
||||
programId: owner,
|
||||
});
|
||||
|
||||
return { account, instruction };
|
||||
}
|
||||
|
||||
|
||||
const MINT_LAYOUT = struct([blob(44), u8('decimals'), blob(37)]);
|
||||
|
||||
export async function getMintDecimals(
|
||||
|
@ -180,16 +190,14 @@ function throwIfNull<T>(value: T | null, message = 'account not found'): T {
|
|||
return value;
|
||||
}
|
||||
|
||||
|
||||
export function uiToNative(amount: number, decimals: number): BN {
|
||||
return new BN(Math.round(amount * Math.pow(10, decimals)))
|
||||
return new BN(Math.round(amount * Math.pow(10, decimals)));
|
||||
}
|
||||
|
||||
export function nativeToUi(amount: number, decimals: number): number {
|
||||
return amount / Math.pow(10, decimals)
|
||||
return amount / Math.pow(10, decimals);
|
||||
}
|
||||
|
||||
|
||||
export async function getFilteredProgramAccounts(
|
||||
connection: Connection,
|
||||
programId: PublicKey,
|
||||
|
@ -221,24 +229,25 @@ export async function getFilteredProgramAccounts(
|
|||
}
|
||||
|
||||
export async function promiseUndef(): Promise<undefined> {
|
||||
return undefined
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export const getUnixTs = () => {
|
||||
return new Date().getTime() / 1000;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
export const ACCOUNT_LAYOUT = struct([
|
||||
blob(32, 'mint'),
|
||||
blob(32, 'owner'),
|
||||
nu64('amount'),
|
||||
blob(93)
|
||||
blob(93),
|
||||
]);
|
||||
|
||||
export function parseTokenAccountData(
|
||||
data: Buffer,
|
||||
): { mint: PublicKey; owner: PublicKey; amount: number } {
|
||||
export function parseTokenAccountData(data: Buffer): {
|
||||
mint: PublicKey;
|
||||
owner: PublicKey;
|
||||
amount: number;
|
||||
} {
|
||||
let { mint, owner, amount } = ACCOUNT_LAYOUT.decode(data);
|
||||
return {
|
||||
mint: new PublicKey(mint),
|
||||
|
@ -247,69 +256,76 @@ export function parseTokenAccountData(
|
|||
};
|
||||
}
|
||||
|
||||
export function parseTokenAccount(
|
||||
data: Buffer
|
||||
): { mint: PublicKey; owner: PublicKey; amount: BN } {
|
||||
|
||||
const decoded = AccountLayout.decode(data)
|
||||
export function parseTokenAccount(data: Buffer): {
|
||||
mint: PublicKey;
|
||||
owner: PublicKey;
|
||||
amount: BN;
|
||||
} {
|
||||
const decoded = AccountLayout.decode(data);
|
||||
return {
|
||||
mint: decoded.mint,
|
||||
owner: decoded.owner,
|
||||
amount: decoded.amount
|
||||
}
|
||||
amount: decoded.amount,
|
||||
};
|
||||
}
|
||||
|
||||
export async function getMultipleAccounts(
|
||||
connection: Connection,
|
||||
publicKeys: PublicKey[],
|
||||
commitment?: Commitment
|
||||
commitment?: Commitment,
|
||||
): Promise<{ publicKey: PublicKey; accountInfo: AccountInfo<Buffer> }[]> {
|
||||
const publickKeyStrs = publicKeys.map((pk) => (pk.toBase58()));
|
||||
const publickKeyStrs = publicKeys.map((pk) => pk.toBase58());
|
||||
|
||||
const args = commitment ? [publickKeyStrs, {commitment}] : [publickKeyStrs];
|
||||
const args = commitment ? [publickKeyStrs, { commitment }] : [publickKeyStrs];
|
||||
// @ts-ignore
|
||||
const resp = await connection._rpcRequest('getMultipleAccounts', args);
|
||||
if (resp.error) {
|
||||
throw new Error(resp.error.message);
|
||||
}
|
||||
return resp.result.value.map(
|
||||
({ data, executable, lamports, owner } , i) => ({
|
||||
publicKey: publicKeys[i],
|
||||
accountInfo: {
|
||||
data: Buffer.from(data[0], 'base64'),
|
||||
executable,
|
||||
owner: new PublicKey(owner),
|
||||
lamports,
|
||||
},
|
||||
}),
|
||||
);
|
||||
return resp.result.value.map(({ data, executable, lamports, owner }, i) => ({
|
||||
publicKey: publicKeys[i],
|
||||
accountInfo: {
|
||||
data: Buffer.from(data[0], 'base64'),
|
||||
executable,
|
||||
owner: new PublicKey(owner),
|
||||
lamports,
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
export async function findLargestTokenAccountForOwner(
|
||||
connection: Connection,
|
||||
owner: PublicKey,
|
||||
mint: PublicKey
|
||||
): Promise<{ publicKey: PublicKey; tokenAccount: { mint: PublicKey; owner: PublicKey; amount: number} }> {
|
||||
|
||||
const response = await connection.getTokenAccountsByOwner(owner, {mint, programId: TOKEN_PROGRAM_ID}, connection.commitment)
|
||||
mint: PublicKey,
|
||||
): Promise<{
|
||||
publicKey: PublicKey;
|
||||
tokenAccount: { mint: PublicKey; owner: PublicKey; amount: number };
|
||||
}> {
|
||||
const response = await connection.getTokenAccountsByOwner(
|
||||
owner,
|
||||
{ mint, programId: TOKEN_PROGRAM_ID },
|
||||
connection.commitment,
|
||||
);
|
||||
let max = -1;
|
||||
let maxTokenAccount: null | { mint: PublicKey; owner: PublicKey; amount: number} = null
|
||||
let maxPubkey: null | PublicKey = null
|
||||
let maxTokenAccount: null | {
|
||||
mint: PublicKey;
|
||||
owner: PublicKey;
|
||||
amount: number;
|
||||
} = null;
|
||||
let maxPubkey: null | PublicKey = null;
|
||||
for (const { pubkey, account } of response.value) {
|
||||
|
||||
const tokenAccount = parseTokenAccountData(account.data)
|
||||
const tokenAccount = parseTokenAccountData(account.data);
|
||||
if (tokenAccount.amount > max) {
|
||||
maxTokenAccount = tokenAccount
|
||||
max = tokenAccount.amount
|
||||
maxPubkey = pubkey
|
||||
maxTokenAccount = tokenAccount;
|
||||
max = tokenAccount.amount;
|
||||
maxPubkey = pubkey;
|
||||
}
|
||||
}
|
||||
|
||||
if (maxPubkey && maxTokenAccount) {
|
||||
return {publicKey: maxPubkey, tokenAccount: maxTokenAccount}
|
||||
return { publicKey: maxPubkey, tokenAccount: maxTokenAccount };
|
||||
} else {
|
||||
throw new Error("No accounts for this token")
|
||||
throw new Error('No accounts for this token');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -344,11 +360,7 @@ const EVENT = struct([
|
|||
u64('clientOrderId'),
|
||||
]);
|
||||
|
||||
|
||||
export function decodeRecentEvents(
|
||||
buffer: Buffer,
|
||||
lastSeenSeqNum?: number,
|
||||
) {
|
||||
export function decodeRecentEvents(buffer: Buffer, lastSeenSeqNum?: number) {
|
||||
const header = EVENT_QUEUE_HEADER.decode(buffer);
|
||||
const nodes: any[] = [];
|
||||
|
||||
|
@ -357,15 +369,17 @@ export function decodeRecentEvents(
|
|||
(buffer.length - EVENT_QUEUE_HEADER.span) / EVENT.span,
|
||||
);
|
||||
|
||||
const newEventsCount = header.seqNum - lastSeenSeqNum
|
||||
const newEventsCount = header.seqNum - lastSeenSeqNum;
|
||||
|
||||
for (let i = newEventsCount; i > 0; --i) {
|
||||
const nodeIndex = (header.head + header.count + allocLen - i) % allocLen
|
||||
const decodedItem = EVENT.decode(buffer, EVENT_QUEUE_HEADER.span + nodeIndex * EVENT.span)
|
||||
nodes.push(decodedItem)
|
||||
const nodeIndex = (header.head + header.count + allocLen - i) % allocLen;
|
||||
const decodedItem = EVENT.decode(
|
||||
buffer,
|
||||
EVENT_QUEUE_HEADER.span + nodeIndex * EVENT.span,
|
||||
);
|
||||
nodes.push(decodedItem);
|
||||
}
|
||||
}
|
||||
|
||||
return { header, nodes };
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue