removed Wallet capability

This commit is contained in:
dd 2021-02-09 16:05:26 -05:00
parent 6e9cbd5003
commit ca32f542ee
5 changed files with 35 additions and 420 deletions

View File

@ -45,10 +45,7 @@
"prettier": "^2.0.5",
"ts-jest": "^26.2.0",
"ts-node": "^9.1.1",
"typescript": "^4.1.3",
"antd": "^4.12.2",
"react": "^17.0.1",
"react-dom": "^17.0.1"
"typescript": "^4.1.3"
},
"files": [
"lib"
@ -58,11 +55,10 @@
"trailingComma": "all"
},
"dependencies": {
"@project-serum/serum": "^0.13.18",
"@project-serum/serum": "^0.13.20",
"@solana/web3.js": "^0.90.0",
"bn.js": "^5.1.2",
"buffer-layout": "^1.2.0",
"double.js": "^1.0.14",
"@project-serum/sol-wallet-adapter": "^0.1.4"
},
"browserslist": [

View File

@ -13,7 +13,9 @@ import {
import {
encodeMangoInstruction,
MangoGroupLayout,
MarginAccountLayout, NUM_MARKETS, NUM_TOKENS,
MarginAccountLayout,
NUM_MARKETS,
NUM_TOKENS,
WideBits,
} from './layout';
import BN from 'bn.js';
@ -24,10 +26,8 @@ import {
nativeToUi,
uiToNative,
zeroKey,
sendTransaction
} from './utils';
import { Market, OpenOrders } from '@project-serum/serum';
import { Wallet } from '@project-serum/sol-wallet-adapter';
import { TOKEN_PROGRAM_ID } from '@project-serum/serum/lib/token-instructions';
import { Order } from '@project-serum/serum/lib/market';
@ -212,30 +212,8 @@ export class MarginAccount {
}
getCollateralRatio(mangoGroup: MangoGroup, prices: number[]): number {
let assetsVal = 0
let liabsVal = 0
for (let i = 0; i < NUM_TOKENS; i++) {
assetsVal += this.getUiDeposit(mangoGroup, i) * prices[i]
liabsVal += this.getUiBorrow(mangoGroup, i) * prices[i]
}
if (liabsVal === 0) {
return 100
}
if (this.openOrdersAccounts == undefined) {
return assetsVal / liabsVal
}
for (let i = 0; i < NUM_MARKETS; i++) {
const openOrdersAccount = this.openOrdersAccounts[i]
if (openOrdersAccount == undefined) {
continue
}
assetsVal += nativeToUi(openOrdersAccount.baseTokenTotal.toNumber(), mangoGroup.mintDecimals[i]) * prices[i]
assetsVal += nativeToUi(openOrdersAccount.quoteTokenTotal.toNumber(), mangoGroup.mintDecimals[NUM_TOKENS-1])
}
const assetsVal = this.getAssetsVal(mangoGroup, prices)
const liabsVal = this.getLiabsVal(mangoGroup, prices)
return assetsVal / liabsVal
}
@ -249,53 +227,35 @@ export class MangoClient {
async sendTransaction(
connection: Connection,
transaction: Transaction,
payer: Account | Wallet,
payer: Account,
additionalSigners: Account[],
notifications?: {sendingMessage: string, sentMessage: string, successMessage: string}
): Promise<TransactionSignature> {
transaction.recentBlockhash = (await connection.getRecentBlockhash('max')).blockhash
transaction.setSigners(payer.publicKey, ...additionalSigners.map( a => a.publicKey ))
// TODO test on mainnet
// if Wallet was provided, sign with wallet
if (!(payer instanceof Account)) {
// TODO test with wallet
transaction.recentBlockhash = (await connection.getRecentBlockhash('max')).blockhash
transaction.setSigners(payer.publicKey, ...additionalSigners.map( a => a.publicKey ))
let args = {
transaction,
wallet: payer,
signers: additionalSigners,
connection,
}
if (notifications) {
args = {...args, ...notifications}
}
const signers = [payer].concat(additionalSigners)
transaction.sign(...signers)
const rawTransaction = transaction.serialize()
return await sendAndConfirmRawTransaction(connection, rawTransaction, {skipPreflight: true})
return await sendTransaction(args)
} else {
// otherwise sign with the payer account
const signers = [payer].concat(additionalSigners)
transaction.sign(...signers)
const rawTransaction = transaction.serialize()
return await sendAndConfirmRawTransaction(connection, rawTransaction, {skipPreflight: true})
}
}
async initMarginAccount(
connection: Connection,
programId: PublicKey,
dexProgramId: PublicKey, // Serum DEX program ID
mangoGroup: MangoGroup,
payer: Account | Wallet,
owner: Account, // assumed to be same as payer for now
): Promise<PublicKey> {
// Create a Solana account for the MarginAccount and allocate space
const accInstr = await createAccountInstruction(connection, payer.publicKey, MarginAccountLayout.span, programId)
const accInstr = await createAccountInstruction(connection,
owner.publicKey, MarginAccountLayout.span, programId)
// Specify the accounts this instruction takes in (see program/src/instruction.rs)
const keys = [
{ isSigner: false, isWritable: false, pubkey: mangoGroup.publicKey},
{ isSigner: false, isWritable: false, pubkey: mangoGroup.publicKey },
{ isSigner: false, isWritable: true, pubkey: accInstr.account.publicKey },
{ isSigner: true, isWritable: false, pubkey: payer.publicKey },
{ isSigner: true, isWritable: false, pubkey: owner.publicKey },
{ isSigner: false, isWritable: false, pubkey: SYSVAR_RENT_PUBKEY }
]
@ -313,18 +273,8 @@ export class MangoClient {
accInstr.account,
]
let notifications;
if (!(payer instanceof Account)) {
const functionName = 'InitMarginAccount'
notifications = {
sendingMessage: `sending ${functionName} instruction...`,
sentMessage: `${functionName} instruction sent`,
successMessage: `${functionName} instruction success`
}
}
// sign, send and confirm transaction
await this.sendTransaction(connection, transaction, payer, additionalSigners, notifications)
await this.sendTransaction(connection, transaction, owner, additionalSigners)
return accInstr.account.publicKey
}
@ -334,7 +284,7 @@ export class MangoClient {
programId: PublicKey,
mangoGroup: MangoGroup,
marginAccount: MarginAccount,
owner: Account | Wallet,
owner: Account,
token: PublicKey,
tokenAcc: PublicKey,
@ -362,16 +312,7 @@ export class MangoClient {
transaction.add(instruction)
const additionalSigners = []
let notifications;
if (!(owner instanceof Account)) {
const functionName = 'Deposit'
notifications = {
sendingMessage: `sending ${functionName} instruction...`,
sentMessage: `${functionName} instruction sent`,
successMessage: `${functionName} instruction success`
}
}
return await this.sendTransaction(connection, transaction, owner, additionalSigners, notifications)
return await this.sendTransaction(connection, transaction, owner, additionalSigners)
}
async withdraw(
@ -379,7 +320,7 @@ export class MangoClient {
programId: PublicKey,
mangoGroup: MangoGroup,
marginAccount: MarginAccount,
owner: Account | Wallet,
owner: Account,
token: PublicKey,
tokenAcc: PublicKey,
@ -418,7 +359,7 @@ export class MangoClient {
programId: PublicKey,
mangoGroup: MangoGroup,
marginAccount: MarginAccount,
owner: Account | Wallet,
owner: Account,
token: PublicKey,
quantity: number
@ -452,7 +393,7 @@ export class MangoClient {
programId: PublicKey,
mangoGroup: MangoGroup,
marginAccount: MarginAccount,
owner: Account | Wallet,
owner: Account,
token: PublicKey,
quantity: number
@ -484,7 +425,7 @@ export class MangoClient {
programId: PublicKey,
mangoGroup: MangoGroup,
liqeeMarginAccount: MarginAccount, // liquidatee marginAccount
liqor: Account | Wallet, // liquidator
liqor: Account, // liquidator
tokenAccs: PublicKey[],
depositQuantities: number[]
): Promise<TransactionSignature> {
@ -519,7 +460,7 @@ export class MangoClient {
mangoGroup: MangoGroup,
marginAccount: MarginAccount,
spotMarket: Market,
owner: Account | Wallet,
owner: Account,
side: 'buy' | 'sell',
price: number,
@ -609,7 +550,7 @@ export class MangoClient {
programId: PublicKey,
mangoGroup: MangoGroup,
marginAccount: MarginAccount,
owner: Account | Wallet,
owner: Account,
spotMarket: Market,
): Promise<TransactionSignature> {
@ -659,7 +600,7 @@ export class MangoClient {
programId: PublicKey,
mangoGroup: MangoGroup,
marginAccount: MarginAccount,
owner: Account | Wallet,
owner: Account,
spotMarket: Market,
order: Order,
): Promise<TransactionSignature> {
@ -740,7 +681,7 @@ export class MangoClient {
connection: Connection,
programId: PublicKey,
mangoGroup: MangoGroup,
owner: Account | Wallet
owner: Account
): Promise<MarginAccount[]> {
const filters = [
@ -766,7 +707,8 @@ export class MangoClient {
const accounts = await getFilteredProgramAccounts(connection, programId, filters);
return accounts.map(
({ publicKey, accountInfo }) =>
new MarginAccount(publicKey, MarginAccountLayout.decode(accountInfo == null ? undefined : accountInfo.data))
new MarginAccount(publicKey, MarginAccountLayout.decode(
accountInfo == null ? undefined : accountInfo.data))
);
}
}

View File

@ -1,5 +1,4 @@
import {struct, u8, blob, union, u32, Layout, bits, Blob, seq, BitStructure, UInt } from 'buffer-layout';
import { bits, BitStructure, Blob, Layout, seq, struct, u32, u8, UInt, union } from 'buffer-layout';
import { PublicKey } from '@solana/web3.js';
import BN from 'bn.js';

View File

@ -1,33 +0,0 @@
import React from "react";
import { notification } from "antd";
// import Link from '../components/Link';
export function notify({
message = "",
description = undefined as any,
txid = "",
type = "info",
placement = "bottomLeft",
}) {
if (txid) {
// <Link
// external
// to={'https://explorer.solana.com/tx/' + txid}
// style={{ color: '#0000ff' }}
// >
// View transaction {txid.slice(0, 8)}...{txid.slice(txid.length - 8)}
// </Link>
description = <></>;
}
(notification as any)[type]({
message: <span style={{ color: "black" }}>{message}</span>,
description: (
<span style={{ color: "black", opacity: 0.5 }}>{description}</span>
),
placement,
style: {
backgroundColor: "white",
},
});
}

View File

@ -1,18 +1,8 @@
import {
Account, Commitment,
Connection,
PublicKey, RpcResponseAndContext, SimulatedTransactionResponse,
SystemProgram,
Transaction,
TransactionInstruction,
TransactionSignature,
} from '@solana/web3.js';
import { Account, Connection, PublicKey, SystemProgram, TransactionInstruction } from '@solana/web3.js';
import { publicKeyLayout, u64 } from './layout';
import BN from 'bn.js';
import { WRAPPED_SOL_MINT } from '@project-serum/serum/lib/token-instructions';
import { blob, struct, u8 } from 'buffer-layout';
import {notify} from "./notifications";
export const zeroKey = new PublicKey(new Uint8Array(32))
@ -142,283 +132,4 @@ export function nativeToUi(amount: number, decimals: number): number {
return amount / Math.pow(10, decimals)
}
const DEFAULT_TIMEOUT = 15000;
export async function sendTransaction({
transaction,
wallet,
signers = [],
connection,
sendingMessage = 'Sending transaction...',
sentMessage = 'Transaction sent',
successMessage = 'Transaction confirmed',
timeout = DEFAULT_TIMEOUT,
}: {
transaction: Transaction;
wallet: any;
signers?: Array<Account>;
connection: Connection;
sendingMessage?: string;
sentMessage?: string;
successMessage?: string;
timeout?: number;
}) {
const signedTransaction = await signTransaction({
transaction,
wallet,
signers,
connection,
});
return await sendSignedTransaction({
signedTransaction,
connection,
sendingMessage,
sentMessage,
successMessage,
timeout,
});
}
export async function signTransaction({
transaction,
wallet,
signers = [],
connection,
}: {
transaction: Transaction;
wallet: any;
signers?: Array<Account>;
connection: Connection;
}) {
transaction.recentBlockhash = (
await connection.getRecentBlockhash('max')
).blockhash;
transaction.setSigners(wallet.publicKey, ...signers.map((s) => s.publicKey));
if (signers.length > 0) {
transaction.partialSign(...signers);
}
return await wallet.signTransaction(transaction);
}
export async function signTransactions({
transactionsAndSigners,
wallet,
connection,
}: {
transactionsAndSigners: {
transaction: Transaction;
signers?: Array<Account>;
}[];
wallet: any;
connection: Connection;
}) {
const blockhash = (await connection.getRecentBlockhash('max')).blockhash;
transactionsAndSigners.forEach(({ transaction, signers = [] }) => {
transaction.recentBlockhash = blockhash;
transaction.setSigners(
wallet.publicKey,
...signers.map((s) => s.publicKey),
);
if (signers && signers.length > 0) {
transaction.partialSign(...signers);
}
});
return await wallet.signAllTransactions(
transactionsAndSigners.map(({ transaction }) => transaction),
);
}
export const getUnixTs = () => {
return new Date().getTime() / 1000;
};
export async function sendSignedTransaction({
signedTransaction,
connection,
sendingMessage = 'Sending transaction...',
sentMessage = 'Transaction sent',
successMessage = 'Transaction confirmed',
timeout = DEFAULT_TIMEOUT,
}: {
signedTransaction: Transaction;
connection: Connection;
sendingMessage?: string;
sentMessage?: string;
successMessage?: string;
timeout?: number;
}): Promise<string> {
const rawTransaction = signedTransaction.serialize();
const startTime = getUnixTs();
notify({ message: sendingMessage });
const txid: TransactionSignature = await connection.sendRawTransaction(
rawTransaction,
{
skipPreflight: true,
},
);
notify({ message: sentMessage, type: 'success', txid });
console.log('Started awaiting confirmation for', txid);
let done = false;
(async () => {
while (!done && getUnixTs() - startTime < timeout) {
connection.sendRawTransaction(rawTransaction, {
skipPreflight: true,
});
await sleep(300);
}
})();
try {
await awaitTransactionSignatureConfirmation(txid, timeout, connection);
} catch (err) {
if (err.timeout) {
throw new Error('Timed out awaiting confirmation on transaction');
}
let simulateResult: SimulatedTransactionResponse | null = null;
try {
simulateResult = (
await simulateTransaction(connection, signedTransaction, 'single')
).value;
} catch (e) {}
if (simulateResult && simulateResult.err) {
if (simulateResult.logs) {
for (let i = simulateResult.logs.length - 1; i >= 0; --i) {
const line = simulateResult.logs[i];
if (line.startsWith('Program log: ')) {
throw new Error(
'Transaction failed: ' + line.slice('Program log: '.length),
);
}
}
}
throw new Error(JSON.stringify(simulateResult.err));
}
throw new Error('Transaction failed');
} finally {
done = true;
}
notify({ message: successMessage, type: 'success', txid });
console.log('Latency', txid, getUnixTs() - startTime);
return txid;
}
async function awaitTransactionSignatureConfirmation(
txid: TransactionSignature,
timeout: number,
connection: Connection,
) {
let done = false;
const result = await new Promise((resolve, reject) => {
(async () => {
setTimeout(() => {
if (done) {
return;
}
done = true;
console.log('Timed out for txid', txid);
reject({ timeout: true });
}, timeout);
try {
connection.onSignature(
txid,
(result) => {
console.log('WS confirmed', txid, result);
done = true;
if (result.err) {
reject(result.err);
} else {
resolve(result);
}
},
'recent',
);
console.log('Set up WS connection', txid);
} catch (e) {
done = true;
console.log('WS error in setup', txid, e);
}
while (!done) {
// eslint-disable-next-line no-loop-func
(async () => {
try {
const signatureStatuses = await connection.getSignatureStatuses([
txid,
]);
const result = signatureStatuses && signatureStatuses.value[0];
if (!done) {
if (!result) {
console.log('REST null result for', txid, result);
} else if (result.err) {
console.log('REST error for', txid, result);
done = true;
reject(result.err);
} else if (!result.confirmations) {
console.log('REST no confirmations for', txid, result);
} else {
console.log('REST confirmation for', txid, result);
done = true;
resolve(result);
}
}
} catch (e) {
if (!done) {
console.log('REST connection error: txid', txid, e);
}
}
})();
await sleep(300);
}
})();
});
done = true;
return result;
}
function mergeTransactions(transactions: (Transaction | undefined)[]) {
const transaction = new Transaction();
transactions
.filter((t): t is Transaction => t !== undefined)
.forEach((t) => {
transaction.add(t);
});
return transaction;
}
export async function sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
/** Copy of Connection.simulateTransaction that takes a commitment parameter. */
async function simulateTransaction(
connection: Connection,
transaction: Transaction,
commitment: Commitment,
): Promise<RpcResponseAndContext<SimulatedTransactionResponse>> {
// @ts-ignore
transaction.recentBlockhash = await connection._recentBlockhash(
// @ts-ignore
connection._disableBlockhashCaching,
);
const signData = transaction.serializeMessage();
// @ts-ignore
const wireTransaction = transaction._serialize(signData);
const encodedTransaction = wireTransaction.toString('base64');
const config: any = { encoding: 'base64', commitment };
const args = [encodedTransaction, config];
// @ts-ignore
const res = await connection._rpcRequest('simulateTransaction', args);
if (res.error) {
throw new Error('failed to simulate transaction: ' + res.error.message);
}
return res.result;
}
}