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

Refactoring for more modularity #58

Draft
wants to merge 23 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
6e8d4c0
typo fixes
ochaloup Aug 23, 2023
5b8771d
Exporting default addresses as constants from SDK
ochaloup Aug 23, 2023
fc854a7
[IN PROGRESS] refactoring to be better tree shakable
ochaloup Aug 26, 2023
59510cd
fixing PR points + making tests running
ochaloup Oct 16, 2023
4249185
chore: don't import bn from two different places
AlexStefan Nov 2, 2023
6cfa996
[test] increasing timeout for unit tests
ochaloup Nov 3, 2023
0a7bdc4
chore: update spl-stake-pool dep
AlexStefan Nov 3, 2023
b929056
chore: remove unused props
AlexStefan Nov 3, 2023
5858f89
chore: unify @coral-xyz/anchor imports
AlexStefan Nov 3, 2023
506be0d
chore: more code simplification
AlexStefan Nov 3, 2023
215ac32
chore: missing change
AlexStefan Nov 4, 2023
c6dae3b
chore: remove some redundant code
AlexStefan Dec 17, 2023
cabb211
chore: add warning to the liquid unstake method
AlexStefan Dec 17, 2023
a0c5d46
feat: refactor program initialisation
AlexStefan Dec 17, 2023
5959e6b
chore: rollback test-world provider change
AlexStefan Dec 17, 2023
0ebbc63
fix: program inits
AlexStefan Dec 17, 2023
29af921
Merge branch 'main' into refactoring-more-modularity
ochaloup Dec 19, 2023
c394167
merge fixes
ochaloup Dec 19, 2023
bf799ef
fix on Dockerfile to not print bigint-buffer errors
ochaloup Dec 19, 2023
ddbf1f3
directed stake sdk to use require pubkey
ochaloup Dec 19, 2023
f9361d8
jsdoc for marinde.ts
ochaloup Dec 19, 2023
3ac3543
Merge remote-tracking branch 'origin/main' into refactoring-more-modu…
AlexStefan Feb 11, 2024
b2ed80e
fix: changes after merge
AlexStefan Feb 11, 2024
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
58 changes: 22 additions & 36 deletions src/config/marinade-config.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,22 @@
import { web3 } from '@coral-xyz/anchor'

const DEFAULT_PROVIDER_URL = 'https://api.devnet.solana.com'

export class MarinadeConfig {
marinadeFinanceProgramId = new web3.PublicKey(
'MarBmsSgKXdrN1egZf5sqe1TMai9K1rChYNDJgjq7aD'
)
marinadeReferralProgramId = new web3.PublicKey(
'MR2LqxoSbw831bNy68utpu5n4YqBH3AzDmddkgk9LQv'
)

marinadeStateAddress = new web3.PublicKey(
'8szGkuLTAux9XMgZ2vtY39jVSowEcpBfFfD8hXSEqdGC'
)
marinadeReferralGlobalStateAddress = new web3.PublicKey(
'MRSh4rUNrpn7mjAq9ENHV4rvwwPKMij113ScZq3twp2'
)

stakeWithdrawAuthPDA = new web3.PublicKey(
'9eG63CdHjsfhHmobHgLtESGC8GabbmRcaSpHAZrtmhco'
)

lookupTableAddress = new web3.PublicKey(
'DCcQeBaCiYsEsjjmEsSYPCr9o4n174LKqXNDvQT5wVd8'
)

connection = new web3.Connection(DEFAULT_PROVIDER_URL)
publicKey: web3.PublicKey | null = null

referralCode: web3.PublicKey | null = null

constructor(configOverrides: Partial<MarinadeConfig> = {}) {
Object.assign(this, configOverrides)
}
}
import { PublicKey } from '@solana/web3.js'

export const DEFAULT_PROVIDER_URL = 'https://api.devnet.solana.com'

export const DEFAULT_MARINADE_PROGRAM_ID = new PublicKey(
'MarBmsSgKXdrN1egZf5sqe1TMai9K1rChYNDJgjq7aD'
)
export const DEFAULT_MARINADE_REFERRAL_PROGRAM_ID = new PublicKey(
'MR2LqxoSbw831bNy68utpu5n4YqBH3AzDmddkgk9LQv'
)
export const DEFAULT_MARINADE_STATE_ADDRESS = new PublicKey(
'8szGkuLTAux9XMgZ2vtY39jVSowEcpBfFfD8hXSEqdGC'
)
export const DEFAULT_MARINADE_REFERRAL_GLOBAL_STATE_ADDRESS = new PublicKey(
'MRSh4rUNrpn7mjAq9ENHV4rvwwPKMij113ScZq3twp2'
)
// export const DEFAULT_STAKE_WITHDRAW_AUTH_PDA = new PublicKey(
// '9eG63CdHjsfhHmobHgLtESGC8GabbmRcaSpHAZrtmhco'
// )
export const LOOKUP_TABLE_ADDRESS = new PublicKey(
'DCcQeBaCiYsEsjjmEsSYPCr9o4n174LKqXNDvQT5wVd8'
)
11 changes: 6 additions & 5 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
export { Marinade } from './marinade'
export * from './marinade'
export * from './marinade-native-stake'
export { MarinadeConfig } from './config/marinade-config'
export * from './marinade-directed-stake'
export * from './config/marinade-config'
export * from './marinade-referral-state/marinade-referral-global-state'
export * from './marinade-referral-state/marinade-referral-partner-state'
export * from './marinade-state/marinade-state'
export * as MarinadeBorsh from './marinade-state/borsh/index'
export { MarinadeReferralPartnerState } from './marinade-referral-state/marinade-referral-partner-state'
export { MarinadeState } from './marinade-state/marinade-state'
export { MarinadeMint } from './marinade-mint/marinade-mint'
export * as MarinadeUtils from './util/index'
export { Wallet, BN, web3, Provider } from '@coral-xyz/anchor'
export * as StakePoolHelpers from './util/stake-pool-helpers'
117 changes: 117 additions & 0 deletions src/marinade-directed-stake.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import {
DEFAULT_DIRECTED_STAKE_ROOT,
DirectedStakeSdk,
findVoteRecords,
voteRecordAddress,
withCreateVote,
withRemoveVote,
withUpdateVote,
} from '@marinade.finance/directed-stake-sdk'
import {
Connection,
PublicKey,
Transaction,
TransactionInstruction,
} from '@solana/web3.js'
import { assertNotDefault, assertNotUndefinedAndReturn } from './util/assert'
import {
DirectedStakeResult,
ErrorMessage,
} from './marinade-directed-stake.types'

/**
* Constructs a DirectedStake contract SDK.
*
* @param connection - RPC connection to the Solana cluster
* @param walletPublicKey - the user wallet public key that the SDK works with as default value
* @returns directed stake sdk
*/
export function getDirectedStakeSdk(
connection: Connection,
walletPublicKey: PublicKey | null
): DirectedStakeSdk {
return new DirectedStakeSdk({
connection,
wallet: {
signTransaction: async () => new Promise(() => new Transaction()),
signAllTransactions: async () => new Promise(() => [new Transaction()]),
publicKey: walletPublicKey ?? PublicKey.default,
ochaloup marked this conversation as resolved.
Show resolved Hide resolved
},
})
}

/**
* Creates necessary directed stake voting instructions for the specified validator.
* If the vote address is left undefined the standard delegation strategy is used.
*
* @param {DirectedStakeSdk} directedStakeSdk - The DirectedStakeSdk instance
* @param {PublicKey} validatorVoteAddress - The vote address to identify the validator
*/
export async function createDirectedStakeVoteIx(
directedStakeSdk: DirectedStakeSdk,
validatorVoteAddress?: PublicKey
): Promise<TransactionInstruction | undefined> {
const owner = assertNotUndefinedAndReturn(
directedStakeSdk.program.provider.publicKey,
ErrorMessage.NO_PUBLIC_KEY
)
// default key would mean not defined in the config
assertNotDefault(owner, ErrorMessage.NO_PUBLIC_KEY)
const { voteRecord } = await getUserVoteRecord(directedStakeSdk, owner)

if (!voteRecord) {
if (validatorVoteAddress) {
return (
await withCreateVote({
sdk: directedStakeSdk,
validatorVote: validatorVoteAddress,
})
).instruction
}
return
Copy link
Contributor

Choose a reason for hiding this comment

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

should we convert this into an assert? like the one at line 59?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure what you mean here about assert to be used here.
Do you mean like assert(voteRecord, ErrorMessage.NO_PUBLIC_KEY).

That's not possible currently as how SDK is designed (as far as I understand it). When the voteRecord is not provided to the method then the caller does not expect an error. It's a normal flow of code.
When the instruction is not returned back to the caller the caller behaves differently.
It's not much intuitive I assume. Maybe the way how SDK API is designed here should be adjusted to be clearer.
WDYT?

The caller should behave differently when he provides no pubkey. The original code of SDK is not mine but it's from MJ so I just trying to understand the behaviour and adapt it.

}

if (validatorVoteAddress) {
return (
await withUpdateVote({
sdk: directedStakeSdk,
validatorVote: validatorVoteAddress,
voteRecord: voteRecord.publicKey,
})
).instruction
}

return (
await withRemoveVote({
sdk: directedStakeSdk,
voteRecord: voteRecord.publicKey,
})
).instruction
}

/**
* Fetches the voteRecord of a given user
*
* @param {PublicKey} userPublicKey - The PublicKey of the user
* @param {DirectedStakeSdk} directedStakeSdk - The DirectedStakeSdk instance
* @returns {Promise<{voteRecord: ProgramAccount<DirectedStakeVoteRecord> | undefined, address: PublicKey}>} - The voteRecord and its address
*/
export async function getUserVoteRecord(
directedStakeSdk: DirectedStakeSdk,
userPublicKey: PublicKey
): Promise<DirectedStakeResult.UserVoterRecord> {
const address = voteRecordAddress({
root: new PublicKey(DEFAULT_DIRECTED_STAKE_ROOT),
owner: userPublicKey,
}).address

const voteRecords = await findVoteRecords({
sdk: directedStakeSdk,
owner: userPublicKey,
})

return {
voteRecord: voteRecords.length === 1 ? voteRecords[0] : undefined,
address,
}
}
14 changes: 14 additions & 0 deletions src/marinade-directed-stake.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { ProgramAccount } from '@coral-xyz/anchor'
import { DirectedStakeVoteRecord } from '@marinade.finance/directed-stake-sdk'
import { PublicKey } from '@solana/web3.js'

export const enum ErrorMessage {
NO_PUBLIC_KEY = "User's public key must be provided in the configuration!",
}

export namespace DirectedStakeResult {
export interface UserVoterRecord {
voteRecord: ProgramAccount<DirectedStakeVoteRecord> | undefined
address: PublicKey
}
}
17 changes: 7 additions & 10 deletions src/marinade-mint/marinade-mint.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
import { Provider, web3, BN } from '@coral-xyz/anchor'
import { getMint, Mint } from '@solana/spl-token-3.x'
import { tokenBalanceToNumber } from '../util/conversion'
import { Connection, PublicKey } from '@solana/web3.js'
import BN from 'bn.js'

export class MarinadeMint {
private constructor(
private readonly anchorProvider: Provider,
public readonly address: web3.PublicKey
private readonly connection: Connection,
public readonly address: PublicKey
) {}

static build(
anchorProvider: Provider,
mintAddress: web3.PublicKey
): MarinadeMint {
return new MarinadeMint(anchorProvider, mintAddress)
static build(connection: Connection, mintAddress: PublicKey): MarinadeMint {
return new MarinadeMint(connection, mintAddress)
AlexStefan marked this conversation as resolved.
Show resolved Hide resolved
}

mintInfo = (): Promise<Mint> =>
getMint(this.anchorProvider.connection, this.address)
mintInfo = (): Promise<Mint> => getMint(this.connection, this.address)

/**
* Returns Total supply as a number with decimals
Expand Down
38 changes: 18 additions & 20 deletions src/marinade-referral-state/marinade-referral-global-state.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,22 @@
import { web3 } from '@coral-xyz/anchor'
import { Marinade } from '../marinade'
import { MarinadeReferralStateResponse } from './marinade-referral-state.types'
import {
MarinadeReferralGlobalState,
MarinadeReferralStateResponse,
} from './marinade-referral-state.types'
import { DEFAULT_MARINADE_REFERRAL_GLOBAL_STATE_ADDRESS } from '../config/marinade-config'
import { MarinadeReferralProgram } from '../programs/marinade-referral-program'
import { PublicKey } from '@solana/web3.js'

export class MarinadeReferralGlobalState {
private constructor(
public readonly state: MarinadeReferralStateResponse.GlobalState,
public readonly marinadeReferralProgramId: web3.PublicKey
) {}
export async function fetchReferralGlobalState(
program: MarinadeReferralProgram,
referralGlobalState: PublicKey = DEFAULT_MARINADE_REFERRAL_GLOBAL_STATE_ADDRESS
): Promise<MarinadeReferralGlobalState> {
const globalState = (await program.account.globalState.fetch(
referralGlobalState
)) as unknown as MarinadeReferralStateResponse.GlobalState

AlexStefan marked this conversation as resolved.
Show resolved Hide resolved
static async fetch(marinade: Marinade) {
const { marinadeReferralProgram, config } = marinade

const globalState =
(await marinadeReferralProgram.program.account.globalState.fetch(
config.marinadeReferralGlobalStateAddress
)) as unknown as MarinadeReferralStateResponse.GlobalState

return new MarinadeReferralGlobalState(
globalState,
config.marinadeReferralProgramId
)
return {
programId: program.programId,
address: referralGlobalState,
...globalState,
}
}
66 changes: 40 additions & 26 deletions src/marinade-referral-state/marinade-referral-partner-state.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,45 @@
import { web3 } from '@coral-xyz/anchor'
import { Marinade } from '../marinade'
import { MarinadeReferralStateResponse } from './marinade-referral-state.types'
import {
MarinadeReferralReferralState,
MarinadeReferralStateResponse,
} from './marinade-referral-state.types'
import { MarinadeReferralProgram } from '../programs/marinade-referral-program'
import { PublicKey } from '@solana/web3.js'

export class MarinadeReferralPartnerState {
private constructor(
public readonly state: MarinadeReferralStateResponse.ReferralState,
public readonly referralStateAddress: web3.PublicKey,
public readonly marinadeReferralProgramId: web3.PublicKey
) {}
export async function fetchReferralState(
program: MarinadeReferralProgram,
referralCode: PublicKey
): Promise<MarinadeReferralReferralState> {
const state = (await program.account.referralState.fetch(
referralCode
)) as unknown as MarinadeReferralStateResponse.ReferralState

AlexStefan marked this conversation as resolved.
Show resolved Hide resolved
static async fetch(marinade: Marinade, referralCode?: web3.PublicKey) {
const { marinadeReferralProgram, config } = marinade
const code = referralCode ?? config.referralCode
if (!code) {
throw new Error(
'The Referral Code must be provided in the MarinadeConfigor supplied as an arg!'
)
}
const state =
(await marinadeReferralProgram.program.account.referralState.fetch(
code
)) as unknown as MarinadeReferralStateResponse.ReferralState
return {
address: referralCode,
programId: program.programId,
...state,
}
}

return new MarinadeReferralPartnerState(
state,
code,
config.marinadeReferralProgramId
/**
* Fetch all the referral partners
*/
export async function getReferralPartners(
program: MarinadeReferralProgram
): Promise<MarinadeReferralReferralState[]> {
const accounts = await program.provider.connection.getProgramAccounts(
new PublicKey(program.programId),
{
filters: [
{
dataSize: program.account.referralState.size + 20 + 96, // number of bytes,
},
],
}
)
return accounts.map(acc =>
program.coder.accounts.decode<MarinadeReferralReferralState>(
'referralState',
acc.account.data
)
}
) as MarinadeReferralReferralState[]
}
Loading
Loading