Skip to content

Commit f14b718

Browse files
committed
refactor(sdk-coin-sol): change marinade and jito flags to enum
BREAKING CHANGE: the `isMarinade` setter in transaction builders has been removed. Existing calls to `txBuilder.isMarinade` will have to be replaced with `txBuilder.stakingType(StakingType.MARINADE)`. Ticket: SC-2620
1 parent bb9af1e commit f14b718

11 files changed

+390
-385
lines changed

examples/ts/sol/stake-jito.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@ async function main() {
5959
.sender(account.publicKey.toBase58())
6060
.stakingAddress(JITO_STAKE_POOL_ADDRESS)
6161
.validator(JITO_STAKE_POOL_ADDRESS)
62-
.isJito(true)
63-
.jitoParams({
62+
.stakingTypeParams({
63+
type: 'JITO',
6464
stakePoolData: {
6565
managerFeeAccount: stakePoolAccount.account.data.managerFeeAccount.toString(),
6666
poolMint: stakePoolAccount.account.data.poolMint.toString(),

modules/sdk-coin-sol/src/lib/iface.ts

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -120,18 +120,27 @@ export interface Approve {
120120
};
121121
}
122122

123+
export enum StakingType {
124+
NATIVE = 'NATIVE',
125+
MARINADE = 'MARINADE',
126+
JITO = 'JITO',
127+
}
128+
129+
export interface JitoStakingActivateParams {
130+
stakePoolData: DepositSolStakePoolData;
131+
}
132+
133+
export type StakingActivateExtraParams = JitoStakingActivateParams;
134+
123135
export interface StakingActivate {
124136
type: InstructionBuilderTypes.StakingActivate;
125137
params: {
126138
fromAddress: string;
127139
stakingAddress: string;
128140
amount: string;
129141
validator: string;
130-
isMarinade?: boolean;
131-
isJito?: boolean;
132-
jitoParams?: {
133-
stakePoolData: DepositSolStakePoolData;
134-
};
142+
stakingType: StakingType;
143+
extraParams?: StakingActivateExtraParams;
135144
};
136145
}
137146

@@ -140,20 +149,23 @@ export interface StakingDelegate {
140149
params: { stakingAddress: string; fromAddress: string; validator: string };
141150
}
142151

152+
export interface JitoStakingDeactivateParams {
153+
stakePoolData: WithdrawStakeStakePoolData;
154+
validatorAddress: string;
155+
transferAuthorityAddress: string;
156+
}
157+
158+
export type StakingDeactivateExtraParams = JitoStakingDeactivateParams;
159+
143160
export interface StakingDeactivate {
144161
type: InstructionBuilderTypes.StakingDeactivate;
145162
params: {
146163
fromAddress: string;
147164
stakingAddress: string;
148165
amount?: string;
149166
unstakingAddress?: string;
150-
isMarinade?: boolean;
151-
isJito?: boolean;
152-
jitoParams?: {
153-
stakePoolData: WithdrawStakeStakePoolData;
154-
validatorAddress: string;
155-
transferAuthorityAddress: string;
156-
};
167+
stakingType: StakingType;
168+
extraParams?: StakingDeactivateExtraParams;
157169
recipients?: Recipient[];
158170
};
159171
}

modules/sdk-coin-sol/src/lib/instructionParamsFactory.ts

Lines changed: 127 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,7 @@ import {
2828
import { NotSupported, TransactionType } from '@bitgo/sdk-core';
2929
import { coins, SolCoin } from '@bitgo/statics';
3030
import assert from 'assert';
31-
import {
32-
InstructionBuilderTypes,
33-
JITO_STAKE_POOL_ADDRESS,
34-
ValidInstructionTypesEnum,
35-
walletInitInstructionIndexes,
36-
} from './constants';
31+
import { InstructionBuilderTypes, ValidInstructionTypesEnum, walletInitInstructionIndexes } from './constants';
3732
import {
3833
AtaClose,
3934
AtaInit,
@@ -53,6 +48,7 @@ import {
5348
SetPriorityFee,
5449
CustomInstruction,
5550
Approve,
51+
StakingType,
5652
} from './iface';
5753
import { getInstructionType } from './utils';
5854
import { DepositSolParams, WithdrawStakeParams } from '@solana/spl-stake-pool';
@@ -330,14 +326,6 @@ function parseSendInstructions(
330326
return instructionData;
331327
}
332328

333-
function stakingInstructionsIsMarinade(si: StakingInstructions): boolean {
334-
return !!(si.delegate === undefined && si.depositSol === undefined);
335-
}
336-
337-
function stakingInstructionsIsJito(si: StakingInstructions): boolean {
338-
return !!(si.delegate === undefined && si.depositSol?.stakePool.toString() === JITO_STAKE_POOL_ADDRESS);
339-
}
340-
341329
/**
342330
* Parses Solana instructions to create staking tx and delegate tx instructions params
343331
* Only supports Nonce, StakingActivate and Memo Solana instructions
@@ -403,39 +391,61 @@ function parseStakingActivateInstructions(
403391

404392
validateStakingInstructions(stakingInstructions);
405393

406-
const stakingActivate: StakingActivate = {
407-
type: InstructionBuilderTypes.StakingActivate,
408-
params: {
409-
fromAddress:
410-
stakingInstructions.create?.fromPubkey.toString() ||
411-
stakingInstructions.depositSol?.fundingAccount.toString() ||
412-
'',
413-
stakingAddress:
414-
stakingInstructions.initialize?.stakePubkey.toString() ||
415-
stakingInstructions.depositSol?.stakePool.toString() ||
416-
'',
417-
amount:
418-
stakingInstructions.create?.lamports.toString() || stakingInstructions.depositSol?.lamports.toString() || '',
419-
validator:
420-
stakingInstructions.delegate?.votePubkey.toString() ||
421-
stakingInstructions.initialize?.authorized.staker.toString() ||
422-
stakingInstructions.depositSol?.stakePool.toString() ||
423-
'',
424-
isMarinade: stakingInstructionsIsMarinade(stakingInstructions),
425-
isJito: stakingInstructionsIsJito(stakingInstructions),
426-
...(stakingInstructions.depositSol && stakingInstructionsIsJito(stakingInstructions)
427-
? {
428-
jitoParams: {
429-
stakePoolData: {
430-
managerFeeAccount: stakingInstructions.depositSol.managerFeeAccount.toString(),
431-
poolMint: stakingInstructions.depositSol.poolMint.toString(),
432-
reserveStake: stakingInstructions.depositSol.reserveStake.toString(),
433-
},
434-
},
435-
}
436-
: {}),
437-
},
438-
};
394+
let stakingActivate: StakingActivate | undefined;
395+
// One case per StakingType
396+
if (stakingInstructions.depositSol !== undefined) {
397+
const stakingType = StakingType.JITO;
398+
stakingActivate = {
399+
type: InstructionBuilderTypes.StakingActivate,
400+
params: {
401+
stakingType,
402+
fromAddress: stakingInstructions.depositSol.fundingAccount.toString(),
403+
stakingAddress: stakingInstructions.depositSol.stakePool.toString(),
404+
amount: stakingInstructions.depositSol.lamports.toString(),
405+
validator: stakingInstructions.depositSol.stakePool.toString(),
406+
extraParams: {
407+
stakePoolData: {
408+
managerFeeAccount: stakingInstructions.depositSol.managerFeeAccount.toString(),
409+
poolMint: stakingInstructions.depositSol.poolMint.toString(),
410+
reserveStake: stakingInstructions.depositSol.reserveStake.toString(),
411+
},
412+
},
413+
},
414+
};
415+
} else if (
416+
stakingInstructions.delegate === undefined &&
417+
stakingInstructions.depositSol === undefined &&
418+
stakingInstructions.create !== undefined &&
419+
stakingInstructions.initialize !== undefined
420+
) {
421+
const stakingType = StakingType.MARINADE;
422+
stakingActivate = {
423+
type: InstructionBuilderTypes.StakingActivate,
424+
params: {
425+
stakingType,
426+
fromAddress: stakingInstructions.create.fromPubkey.toString(),
427+
stakingAddress: stakingInstructions.initialize.stakePubkey.toString(),
428+
amount: stakingInstructions.create.lamports.toString(),
429+
validator: stakingInstructions.initialize.authorized.staker.toString(),
430+
},
431+
};
432+
} else if (stakingInstructions.create !== undefined && stakingInstructions.initialize !== undefined) {
433+
const stakingType = StakingType.NATIVE;
434+
stakingActivate = {
435+
type: InstructionBuilderTypes.StakingActivate,
436+
params: {
437+
stakingType,
438+
fromAddress: stakingInstructions.create.fromPubkey.toString(),
439+
stakingAddress: stakingInstructions.initialize.stakePubkey.toString(),
440+
amount: stakingInstructions.create.lamports.toString(),
441+
validator:
442+
stakingInstructions.delegate?.votePubkey.toString() ||
443+
stakingInstructions.initialize.authorized.staker.toString(),
444+
},
445+
};
446+
}
447+
448+
assert(stakingActivate !== undefined, 'Missing stakingActivate');
439449
instructionData.push(stakingActivate);
440450

441451
return instructionData;
@@ -490,19 +500,18 @@ interface StakingInstructions {
490500
}
491501

492502
function validateStakingInstructions(stakingInstructions: StakingInstructions) {
493-
if (stakingInstructionsIsJito(stakingInstructions)) {
494-
if (!stakingInstructions.depositSol) {
495-
throw new NotSupported('Invalid staking activate transaction, missing deposit sol instruction');
496-
}
497-
} else {
498-
if (!stakingInstructions.create) {
499-
throw new NotSupported('Invalid staking activate transaction, missing create stake account instruction');
500-
}
501-
if (!stakingInstructions.delegate && !stakingInstructions.initialize) {
502-
throw new NotSupported(
503-
'Invalid staking activate transaction, missing initialize stake account/delegate instruction'
504-
);
505-
}
503+
if (stakingInstructions.delegate === undefined && stakingInstructions.depositSol !== undefined) {
504+
return;
505+
}
506+
507+
if (!stakingInstructions.create) {
508+
throw new NotSupported('Invalid staking activate transaction, missing create stake account instruction');
509+
}
510+
511+
if (!stakingInstructions.delegate && !stakingInstructions.initialize) {
512+
throw new NotSupported(
513+
'Invalid staking activate transaction, missing initialize stake account/delegate instruction'
514+
);
506515
}
507516
}
508517

@@ -634,50 +643,64 @@ function parseStakingDeactivateInstructions(
634643

635644
for (const unstakingInstruction of unstakingInstructions) {
636645
validateUnstakingInstructions(unstakingInstruction);
637-
const isMarinade =
638-
unstakingInstruction.deactivate === undefined && unstakingInstruction.withdrawStake === undefined;
639-
const stakingDeactivate: StakingDeactivate = {
640-
type: InstructionBuilderTypes.StakingDeactivate,
641-
params: {
642-
fromAddress:
643-
unstakingInstruction.deactivate?.authorizedPubkey.toString() ||
644-
unstakingInstruction.withdrawStake?.destinationStakeAuthority.toString() ||
645-
'',
646-
stakingAddress:
647-
unstakingInstruction.split?.stakePubkey.toString() ||
648-
unstakingInstruction.deactivate?.stakePubkey.toString() ||
649-
unstakingInstruction.withdrawStake?.stakePool.toString() ||
650-
'',
651-
amount:
652-
unstakingInstruction.split?.lamports.toString() || unstakingInstruction.withdrawStake?.poolTokens.toString(),
653-
unstakingAddress:
654-
unstakingInstruction.split?.splitStakePubkey.toString() ||
655-
unstakingInstruction.withdrawStake?.destinationStake.toString(),
656-
isMarinade: isMarinade,
657-
recipients: isMarinade
658-
? [
659-
{
660-
address: unstakingInstruction.transfer?.toPubkey.toString() || '',
661-
amount: unstakingInstruction.transfer?.lamports.toString() || '',
662-
},
663-
]
664-
: undefined,
665-
...(unstakingInstruction.withdrawStake !== undefined
666-
? {
667-
isJito: unstakingInstruction.withdrawStake !== undefined,
668-
jitoParams: unstakingInstruction.withdrawStake && {
669-
stakePoolData: {
670-
managerFeeAccount: unstakingInstruction.withdrawStake.managerFeeAccount.toString(),
671-
poolMint: unstakingInstruction.withdrawStake.poolMint.toString(),
672-
validatorList: unstakingInstruction.withdrawStake.validatorList.toString(),
673-
},
674-
validatorAddress: unstakingInstruction.withdrawStake.validatorStake.toString(),
675-
transferAuthorityAddress: unstakingInstruction.withdrawStake.sourceTransferAuthority.toString(),
676-
},
677-
}
678-
: {}),
679-
},
680-
};
646+
647+
let stakingDeactivate: StakingDeactivate | undefined;
648+
// One case per StakingType
649+
if (unstakingInstruction.withdrawStake !== undefined) {
650+
const stakingType = StakingType.JITO;
651+
stakingDeactivate = {
652+
type: InstructionBuilderTypes.StakingDeactivate,
653+
params: {
654+
stakingType,
655+
fromAddress: unstakingInstruction.withdrawStake.destinationStakeAuthority.toString(),
656+
stakingAddress: unstakingInstruction.withdrawStake.stakePool.toString(),
657+
amount: unstakingInstruction.withdrawStake.poolTokens.toString(),
658+
unstakingAddress: unstakingInstruction.withdrawStake.destinationStake.toString(),
659+
extraParams: {
660+
stakePoolData: {
661+
managerFeeAccount: unstakingInstruction.withdrawStake.managerFeeAccount.toString(),
662+
poolMint: unstakingInstruction.withdrawStake.poolMint.toString(),
663+
validatorListAccount: unstakingInstruction.withdrawStake.validatorList.toString(),
664+
},
665+
validatorAddress: unstakingInstruction.withdrawStake.validatorStake.toString(),
666+
transferAuthorityAddress: unstakingInstruction.withdrawStake.sourceTransferAuthority.toString(),
667+
},
668+
},
669+
};
670+
} else if (unstakingInstruction.deactivate === undefined && unstakingInstruction.transfer !== undefined) {
671+
const stakingType = StakingType.MARINADE;
672+
stakingDeactivate = {
673+
type: InstructionBuilderTypes.StakingDeactivate,
674+
params: {
675+
stakingType,
676+
fromAddress: '',
677+
stakingAddress: '',
678+
recipients: [
679+
{
680+
address: unstakingInstruction.transfer.toPubkey.toString() || '',
681+
amount: unstakingInstruction.transfer.lamports.toString() || '',
682+
},
683+
],
684+
},
685+
};
686+
} else {
687+
const stakingType = StakingType.NATIVE;
688+
stakingDeactivate = {
689+
type: InstructionBuilderTypes.StakingDeactivate,
690+
params: {
691+
stakingType,
692+
fromAddress: unstakingInstruction.deactivate?.authorizedPubkey.toString() || '',
693+
stakingAddress:
694+
unstakingInstruction.split?.stakePubkey.toString() ||
695+
unstakingInstruction.deactivate?.stakePubkey.toString() ||
696+
'',
697+
amount: unstakingInstruction.split?.lamports.toString(),
698+
unstakingAddress: unstakingInstruction.split?.splitStakePubkey.toString(),
699+
},
700+
};
701+
}
702+
703+
assert(stakingDeactivate !== undefined, 'Missing stakingDeactivate');
681704
instructionData.push(stakingDeactivate);
682705
}
683706

modules/sdk-coin-sol/src/lib/jitoStakePoolOperations.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export interface StakePoolData {
4747
staker: string;
4848
stakeDepositAuthority: string;
4949
stakeWithdrawBumpSeed: number;
50-
validatorList: string;
50+
validatorListAccount: string;
5151
reserveStake: string;
5252
poolMint: string;
5353
managerFeeAccount: string;
@@ -209,7 +209,7 @@ export interface WithdrawStakeInstructionsParams {
209209
poolAmount: string;
210210
}
211211

212-
export type WithdrawStakeStakePoolData = Pick<StakePoolData, 'poolMint' | 'validatorList' | 'managerFeeAccount'>;
212+
export type WithdrawStakeStakePoolData = Pick<StakePoolData, 'poolMint' | 'validatorListAccount' | 'managerFeeAccount'>;
213213

214214
/**
215215
* Construct Solana depositSol stake pool instruction from parameters.
@@ -234,7 +234,7 @@ export function withdrawStakeInstructions(
234234
} = params;
235235

236236
const poolMint = new PublicKey(stakePool.poolMint);
237-
const validatorList = new PublicKey(stakePool.validatorList);
237+
const validatorList = new PublicKey(stakePool.validatorListAccount);
238238
const managerFeeAccount = new PublicKey(stakePool.managerFeeAccount);
239239

240240
const poolTokenAccount = getAssociatedTokenAddressSync(poolMint, tokenOwner);
@@ -291,9 +291,9 @@ export function decodeWithdrawStake(instruction: TransactionInstruction): Withdr
291291
const validatorList = parseKey(keys[i++], { isSigner: false, isWritable: true });
292292
const withdrawAuthority = parseKey(keys[i++], { isSigner: false, isWritable: false });
293293
const validatorStake = parseKey(keys[i++], { isSigner: false, isWritable: true });
294-
const destinationStake = keys[i++].pubkey; // parseKey(keys[i++], { isSigner: false, isWritable: true });
295-
const destinationStakeAuthority = keys[i++].pubkey; // parseKey(keys[i++], { isSigner: false, isWritable: false });
296-
const sourceTransferAuthority = keys[i++].pubkey; // parseKey(keys[i++], { isSigner: true, isWritable: false });
294+
const destinationStake = keys[i++].pubkey;
295+
const destinationStakeAuthority = keys[i++].pubkey;
296+
const sourceTransferAuthority = parseKey(keys[i++], { isSigner: true, isWritable: false });
297297
const sourcePoolAccount = parseKey(keys[i++], { isSigner: false, isWritable: true });
298298
const managerFeeAccount = parseKey(keys[i++], { isSigner: false, isWritable: true });
299299
const poolMint = parseKey(keys[i++], { isSigner: false, isWritable: true });

0 commit comments

Comments
 (0)