Add all required bits for stake instruction decoding (#312)
This commit is contained in:
parent
32ba45d16b
commit
9fa5c4e4a9
|
@ -9,6 +9,7 @@ import { useConnection, useSolanaExplorerUrlSuffix } from '../utils/connection';
|
|||
import { useWallet, useWalletPublicKeys } from '../utils/wallet';
|
||||
import NewOrder from './instructions/NewOrder';
|
||||
import UnknownInstruction from './instructions/UnknownInstruction';
|
||||
import StakeInstruction from '../components/instructions/StakeInstruction';
|
||||
import SystemInstruction from '../components/instructions/SystemInstruction';
|
||||
import DexInstruction from '../components/instructions/DexInstruction';
|
||||
import TokenInstruction from '../components/instructions/TokenInstruction';
|
||||
|
@ -215,6 +216,19 @@ export default function SignTransactionFormContent({
|
|||
onOpenAddress={onOpenAddress}
|
||||
/>
|
||||
);
|
||||
case 'stakeAuthorizeWithSeed':
|
||||
case 'stakeAuthorize':
|
||||
case 'stakeDeactivate':
|
||||
case 'stakeDelegate':
|
||||
case 'stakeInitialize':
|
||||
case 'stakeSplit':
|
||||
case 'stakeWithdraw':
|
||||
return (
|
||||
<StakeInstruction
|
||||
instruction={instruction}
|
||||
onOpenAddress={onOpenAddress}
|
||||
/>
|
||||
);
|
||||
case 'newOrder':
|
||||
return (
|
||||
<NewOrder instruction={instruction} onOpenAddress={onOpenAddress} />
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
import React from 'react';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import LabelValue from './LabelValue';
|
||||
|
||||
const TYPE_LABELS = {
|
||||
stakeAuthorizeWithSeed: 'Stake authorize with seed',
|
||||
stakeAuthorize: 'Stake authorize',
|
||||
stakeDeactivate: 'Deactivate stake',
|
||||
stakeDelegate: 'Delegate stake',
|
||||
stakeInitialize: 'Initialize stake',
|
||||
stakeSplit: 'Split stake',
|
||||
stakeWithdraw: 'Withdraw stake',
|
||||
};
|
||||
|
||||
const DATA_LABELS = {
|
||||
stakePubkey: { label: 'Stake', address: true },
|
||||
authorizedStaker: { label: 'Authorized staker', address: true },
|
||||
authorizedWithdrawer: { label: 'Authorized withdrawer', address: true },
|
||||
lockup: { label: 'Lockup', address: false },
|
||||
authorizedPubkey: { label: 'Authorized', address: true },
|
||||
votePubkey: { label: 'Vote', address: true },
|
||||
authorizedSeed: { label: 'Seed', address: false },
|
||||
noncePubkey: { label: 'Nonce', address: true },
|
||||
authorityBase: { label: 'Authority base', address: true },
|
||||
authoritySeed: { label: 'Authority seed', address: false },
|
||||
authorityOwner: { label: 'Authority owner', address: true },
|
||||
newAuthorizedPubkey: { label: 'New authorized', address: true },
|
||||
stakeAuthorizationType: { label: 'Stake authorization type', address: false, transform: () => JSON.stringify },
|
||||
custodianPubkey: { label: 'Custodian', address: true },
|
||||
splitStakePubkey: { label: 'Split to', address: true },
|
||||
lamports: { label: 'Lamports', address: false },
|
||||
};
|
||||
|
||||
export default function StakeInstruction({ instruction, onOpenAddress }) {
|
||||
const { type, data } = instruction;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Typography
|
||||
variant="subtitle1"
|
||||
style={{ fontWeight: 'bold' }}
|
||||
gutterBottom
|
||||
>
|
||||
{TYPE_LABELS[type]}
|
||||
</Typography>
|
||||
{data &&
|
||||
Object.entries(data).map(([key, value]) => {
|
||||
const dataLabel = DATA_LABELS[key];
|
||||
if (!dataLabel) {
|
||||
return null;
|
||||
}
|
||||
const { label, address, transform } = dataLabel;
|
||||
return (
|
||||
<LabelValue
|
||||
key={key}
|
||||
label={label + ''}
|
||||
value={address ? value?.toBase58() : (transform ? transform(value) : value)}
|
||||
link={address}
|
||||
onClick={() => address && onOpenAddress(value?.toBase58())}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import bs58 from 'bs58';
|
||||
import { Message, SystemInstruction, SystemProgram } from '@solana/web3.js';
|
||||
import { Message, StakeInstruction, StakeProgram, SystemInstruction, SystemProgram } from '@solana/web3.js';
|
||||
import {
|
||||
decodeInstruction,
|
||||
decodeTokenInstructionData,
|
||||
|
@ -96,6 +96,9 @@ const toInstruction = async (
|
|||
if (programId.equals(SystemProgram.programId)) {
|
||||
console.log('[' + index + '] Handled as system instruction');
|
||||
return handleSystemInstruction(publicKey, instruction, accountKeys);
|
||||
} else if (programId.equals(StakeProgram.programId)) {
|
||||
console.log('[' + index + '] Handled as stake instruction');
|
||||
return handleStakeInstruction(publicKey, instruction, accountKeys);
|
||||
} else if (programId.equals(TOKEN_PROGRAM_ID)) {
|
||||
console.log('[' + index + '] Handled as token instruction');
|
||||
let decodedInstruction = decodeTokenInstruction(decoded);
|
||||
|
@ -155,7 +158,9 @@ const toInstruction = async (
|
|||
programId,
|
||||
};
|
||||
}
|
||||
} catch {}
|
||||
} catch (e) {
|
||||
console.log(`Failed to decode instruction: ${e}`);
|
||||
}
|
||||
|
||||
// all decodings failed
|
||||
console.log('[' + index + '] Failed, data: ' + JSON.stringify(decoded));
|
||||
|
@ -380,6 +385,74 @@ const handleSystemInstruction = (publicKey, instruction, accountKeys) => {
|
|||
};
|
||||
};
|
||||
|
||||
const handleStakeInstruction = (publicKey, instruction, accountKeys) => {
|
||||
const { programIdIndex, accounts, data } = instruction;
|
||||
if (!programIdIndex || !accounts || !data) {
|
||||
return;
|
||||
}
|
||||
|
||||
// construct stake instruction
|
||||
const stakeInstruction = {
|
||||
programId: accountKeys[programIdIndex],
|
||||
keys: accounts.map((accountIndex) => ({
|
||||
pubkey: accountKeys[accountIndex],
|
||||
})),
|
||||
data: bs58.decode(data),
|
||||
};
|
||||
|
||||
let decoded;
|
||||
const type = StakeInstruction.decodeInstructionType(stakeInstruction);
|
||||
switch (type) {
|
||||
case 'AuthorizeWithSeed':
|
||||
decoded = StakeInstruction.decodeAuthorizeWithSeed(stakeInstruction);
|
||||
break;
|
||||
case 'Authorize':
|
||||
decoded = StakeInstruction.decodeAuthorize(stakeInstruction);
|
||||
break;
|
||||
case 'Deactivate':
|
||||
decoded = StakeInstruction.decodeDeactivate(stakeInstruction);
|
||||
break;
|
||||
case 'Delegate':
|
||||
decoded = StakeInstruction.decodeDelegate(stakeInstruction);
|
||||
break;
|
||||
case 'Initialize':
|
||||
decoded = StakeInstruction.decodeInitialize(stakeInstruction);
|
||||
// Lockup inactive if all zeroes
|
||||
const lockup = decoded.lockup;
|
||||
if (lockup && lockup.unixTimestamp === 0 && lockup.epoch === 0 && lockup.custodian.equals(PublicKey.default)) {
|
||||
decoded.lockup = 'Inactive';
|
||||
}
|
||||
else {
|
||||
decoded.lockup = `unixTimestamp: ${lockup.unixTimestamp}, custodian: ${lockup.epoch}, custodian: ${lockup.custodian.toBase58()}`;
|
||||
}
|
||||
// flatten authorized to allow address render
|
||||
decoded.authorizedStaker = decoded.authorized.staker
|
||||
decoded.authorizedWithdrawer = decoded.authorized.withdrawer
|
||||
delete decoded.authorized
|
||||
break;
|
||||
case 'Split':
|
||||
decoded = StakeInstruction.decodeSplit(stakeInstruction);
|
||||
break;
|
||||
case 'Withdraw':
|
||||
decoded = StakeInstruction.decodeWithdraw(stakeInstruction);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
!decoded ||
|
||||
(decoded.fromPubkey && !publicKey.equals(decoded.fromPubkey))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'stake' + type,
|
||||
data: decoded,
|
||||
};
|
||||
};
|
||||
|
||||
const handleTokenInstruction = (
|
||||
publicKey,
|
||||
accounts,
|
||||
|
|
Loading…
Reference in New Issue