Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sdk 331 account management should be moved to instruments #246

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 41 additions & 51 deletions packages/js/src/plugins/instrumentModule/methods.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import { Leg, QuoteAsset, legBeet } from '@convergence-rfq/rfq';
import { QuoteAsset, legBeet } from '@convergence-rfq/rfq';

import { AccountMeta } from '@solana/web3.js';
import { AccountMeta, PublicKey } from '@solana/web3.js';
import { createSerializerFromFixableBeetArgsStruct } from '../../types';
import { addDecimals } from '../../utils/conversions';
import { toSolitaLegSide } from '../rfqModule/models/LegSide';
import { PsyoptionsEuropeanInstrument } from '../psyoptionsEuropeanInstrumentModule';
import { PsyoptionsAmericanInstrument } from '../psyoptionsAmericanInstrumentModule';
import { SpotLegInstrument } from '../spotInstrumentModule';
import { toSolitaLegSide } from '../rfqModule/models';
import { LegInstrument, QuoteInstrument } from './types';
import { addDecimals } from '@/utils/conversions';
import { Convergence } from '@/Convergence';

export function toLeg(legInstrument: LegInstrument): Leg {
export function serializeAsLeg(legInstrument: LegInstrument) {
const legSerializer = createSerializerFromFixableBeetArgsStruct(legBeet);
return legSerializer.serialize(toLeg(legInstrument));
}

export function toLeg(legInstrument: LegInstrument) {
return {
instrumentProgram: legInstrument.getProgramId(),
baseAssetIndex: legInstrument.getBaseAssetIndex(),
Expand All @@ -24,29 +26,45 @@ export function toLeg(legInstrument: LegInstrument): Leg {
};
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why to move this logic to the instrument interface? The logic in each instrument is the same and is a clone of this logic


export function serializeAsLeg(legInstrument: LegInstrument) {
const legSerializer = createSerializerFromFixableBeetArgsStruct(legBeet);
return legSerializer.serialize(toLeg(legInstrument));
}
export function getBaseAssetAccount(
legInstrument: LegInstrument,
cvg: Convergence
): AccountMeta {
const baseAsset = cvg
.protocol()
.pdas()
.baseAsset({ index: legInstrument.getBaseAssetIndex().value });

export function getSerializedLegLength(legInstrument: LegInstrument) {
return serializeAsLeg(legInstrument).length;
const baseAssetAccount: AccountMeta = {
pubkey: baseAsset,
isSigner: false,
isWritable: false,
};

return baseAssetAccount;
}

export function getProgramAccount(legInstrument: LegInstrument): AccountMeta {
return {
pubkey: legInstrument.getProgramId(),
export async function getOracleAccount(
baseAsset: PublicKey,
cvg: Convergence
): Promise<AccountMeta> {
const baseAssetModel = await cvg
.protocol()
.findBaseAssetByAddress({ address: baseAsset });

if (!baseAssetModel.priceOracle.address) {
throw Error('Base asset does not have a price oracle!');
}
const oracleAccount = {
pubkey: baseAssetModel.priceOracle.address,
isSigner: false,
isWritable: false,
};
return oracleAccount;
}

export function getValidationAccounts(
legInstrument: LegInstrument
): AccountMeta[] {
return [getProgramAccount(legInstrument)].concat(
legInstrument.getValidationAccounts()
);
export function getSerializedLegLength(legInstrument: LegInstrument) {
return serializeAsLeg(legInstrument).length;
}

export function toQuote(legInstrument: QuoteInstrument): QuoteAsset {
Expand All @@ -56,31 +74,3 @@ export function toQuote(legInstrument: QuoteInstrument): QuoteAsset {
instrumentDecimals: legInstrument.getDecimals(),
};
}

//TODO: refactor this method to use instrument interface in the future
export const legToBaseAssetMint = async (
convergence: Convergence,
leg: LegInstrument
) => {
if (leg instanceof PsyoptionsEuropeanInstrument) {
const euroMetaOptionMint = await convergence.tokens().findMintByAddress({
address: leg.optionMint,
});

return euroMetaOptionMint;
} else if (leg instanceof PsyoptionsAmericanInstrument) {
const americanOptionMint = await convergence.tokens().findMintByAddress({
address: leg.optionMint,
});

return americanOptionMint;
} else if (leg instanceof SpotLegInstrument) {
const mint = await convergence.tokens().findMintByAddress({
address: leg.mintAddress,
});

return mint;
}

throw Error('Unsupported instrument!');
};
1 change: 1 addition & 0 deletions packages/js/src/plugins/instrumentModule/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export interface LegInstrument {
getDecimals: () => number;
getSide: () => LegSide;
serializeInstrumentData: () => Buffer;
getExchangeAssetMint(): PublicKey;
getValidationAccounts(): AccountMeta[];
getPreparationsBeforeRfqCreation(): Promise<CreateOptionInstrumentsResult>;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,17 +70,16 @@ export class PsyoptionsAmericanInstrument implements LegInstrument {
readonly stableAssetMint?: PublicKey
) {}

getBaseAssetIndex = () => this.baseAssetIndex;
getAmount = () => this.amount;
getBaseAssetIndex = () => this.baseAssetIndex;
getDecimals = () => PsyoptionsAmericanInstrument.decimals;
getSide = () => this.side;

async getPreparationsBeforeRfqCreation(): Promise<CreateOptionInstrumentsResult> {
if (!this.underlyingAssetMint) {
throw new Error('Missing underlying asset mint');
}
if (!this.stableAssetMint) {
throw new Error('Missing stable asset mint');
}
if (!this.underlyingAssetMint)
throw Error('Underlying asset mint is not defined');

if (!this.stableAssetMint) throw Error('Stable asset mint is not defined');

const optionMarketIxs = await getPsyAmericanMarketIxs(
this.convergence,
Expand All @@ -95,6 +94,10 @@ export class PsyoptionsAmericanInstrument implements LegInstrument {
return optionMarketIxs;
}

getExchangeAssetMint(): PublicKey {
return this.optionMint;
}

static async create(
convergence: Convergence,
underlyingMint: Mint,
Expand Down Expand Up @@ -171,12 +174,10 @@ export class PsyoptionsAmericanInstrument implements LegInstrument {
}

getValidationAccounts() {
if (!this.underlyingAssetMint) {
throw new Error('Missing underlying asset mint');
}
if (!this.stableAssetMint) {
throw new Error('Missing stable asset mint');
}
if (!this.underlyingAssetMint)
throw Error('Underlying asset mint is not defined');
if (!this.stableAssetMint) throw Error('Stable asset mint is not defined');

const mintInfoPda = this.convergence
.rfqs()
.pdas()
Expand All @@ -186,6 +187,11 @@ export class PsyoptionsAmericanInstrument implements LegInstrument {
.pdas()
.mintInfo({ mint: this.stableAssetMint });
return [
{
pubkey: this.getProgramId(),
isSigner: false,
isWritable: false,
},
{ pubkey: this.optionMetaPubKey, isSigner: false, isWritable: false },
{
pubkey: mintInfoPda,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,13 +125,13 @@ export class PsyoptionsEuropeanInstrument implements LegInstrument {
getSide = () => this.side;
async getPreparationsBeforeRfqCreation(): Promise<CreateOptionInstrumentsResult> {
if (!this.underlyingAssetMint) {
throw new Error('Missing underlying asset mint');
throw Error('Underlying asset mint is undefined');
}
if (!this.stableAssetMint) {
throw new Error('Missing stable asset mint');
throw Error('Stable asset mint is undefined');
}
if (!this.oracleAddress) {
throw new Error('Missing oracle address');
throw Error('Oracle address is undefined');
}
const optionMarketIxs = await getPsyEuropeanMarketIxs(
this.convergence,
Expand All @@ -148,6 +148,10 @@ export class PsyoptionsEuropeanInstrument implements LegInstrument {
return optionMarketIxs;
}

getExchangeAssetMint(): PublicKey {
return this.optionMint;
}

static async create(
convergence: Convergence,
underlyingMint: Mint,
Expand Down Expand Up @@ -213,10 +217,14 @@ export class PsyoptionsEuropeanInstrument implements LegInstrument {

/** Helper method to get validation accounts for a Psyoptions European instrument. */
getValidationAccounts() {
if (!this.underlyingAssetMint) {
throw new Error('Missing underlying asset mint');
}
if (!this.underlyingAssetMint)
throw Error('Underlying asset mint is undefined');
return [
{
pubkey: this.getProgramId(),
isSigner: false,
isWritable: false,
},
{ pubkey: this.optionMetaPubKey, isSigner: false, isWritable: false },
{
pubkey: this.convergence
Expand Down
79 changes: 15 additions & 64 deletions packages/js/src/plugins/rfqModule/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { AccountMeta } from '@solana/web3.js';
import { Sha256 } from '@aws-crypto/sha256-js';
import { Leg } from '@convergence-rfq/rfq';
import { AccountMeta } from '@solana/web3.js';
import {
Confirmation,
Quote,
Expand All @@ -9,13 +8,10 @@ import {
isQuoteStandard,
} from '../rfqModule/models';
import { UnparsedAccount } from '../../types';
import { Convergence } from '../../Convergence';
import {
LegInstrument,
getSerializedLegLength,
getValidationAccounts,
serializeAsLeg,
toLeg,
} from '../instrumentModule';
import { LEG_MULTIPLIER_DECIMALS } from './constants';
import { Rfq, Response, isFixedSizeOpen } from './models';
Expand Down Expand Up @@ -81,65 +77,6 @@ export const calculateExpectedLegsSize = (
);
};

// TODO remove
export const instrumentsToLegsAndLegsSize = (
instruments: LegInstrument[]
): [Leg[], number] => {
return [
instrumentsToLegs(instruments),
calculateExpectedLegsSize(instruments),
];
};

export const instrumentsToLegs = (instruments: LegInstrument[]): Leg[] => {
return instruments.map((i) => toLeg(i));
};

// TODO remove
export const instrumentsToLegsAndExpectedLegsHash = (
instruments: LegInstrument[]
): [Leg[], Uint8Array] => {
return [
instrumentsToLegs(instruments),
calculateExpectedLegsHash(instruments),
];
};

export const legsToBaseAssetAccounts = (
convergence: Convergence,
legs: Leg[]
): AccountMeta[] => {
const baseAssetAccounts: AccountMeta[] = [];

for (const leg of legs) {
const baseAsset = convergence
.protocol()
.pdas()
.baseAsset({ index: leg.baseAssetIndex.value });

const baseAssetAccount: AccountMeta = {
pubkey: baseAsset,
isSigner: false,
isWritable: false,
};

baseAssetAccounts.push(baseAssetAccount);
}

return baseAssetAccounts;
};

// TODO remove async part after option instruments refactoring
export const instrumentsToLegAccounts = async (
instruments: LegInstrument[]
): Promise<AccountMeta[]> => {
const accounts = await Promise.all(
instruments.map((i) => getValidationAccounts(i))
);

return accounts.flat();
};

export const sortByActiveAndExpiry = (rfqs: Rfq[]) => {
return rfqs
.sort((a, b) => {
Expand Down Expand Up @@ -188,3 +125,17 @@ export function extractLegsMultiplier(
}
throw new Error('Invalid fixed size');
}

export const removeDuplicateAccountMeta = (
accountMeta: AccountMeta[]
): AccountMeta[] => {
const uniqueAccountMeta: AccountMeta[] = [];
for (let i = 0; i < accountMeta.length; i++) {
if (
!uniqueAccountMeta.find((x) => x.pubkey.equals(accountMeta[i].pubkey))
) {
uniqueAccountMeta.push(accountMeta[i]);
}
}
return uniqueAccountMeta;
};
8 changes: 6 additions & 2 deletions packages/js/src/plugins/rfqModule/models/Rfq.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,16 @@ export const toRfq = async (
convergence: Convergence,
account: RfqAccount
): Promise<Rfq> => {
const quoteAsset = await SpotQuoteInstrument.parseFromQuote(
const quoteAsset = SpotQuoteInstrument.parseFromQuote(
convergence,
account.data.quoteAsset
);
const collateralMint = await collateralMintCache.get(convergence);
const collateralDecimals = collateralMint.decimals;
const legs = account.data.legs.map((leg) =>
convergence.parseLegInstrument(leg)
);

return {
model: 'rfq',
address: account.publicKey,
Expand All @@ -130,6 +134,6 @@ export const toRfq = async (
totalResponses: account.data.totalResponses,
clearedResponses: account.data.clearedResponses,
confirmedResponses: account.data.confirmedResponses,
legs: account.data.legs.map((leg) => convergence.parseLegInstrument(leg)),
legs,
};
};
Loading