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