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 { useWallet, useWalletPublicKeys } from '../utils/wallet';
|
||||||
import NewOrder from './instructions/NewOrder';
|
import NewOrder from './instructions/NewOrder';
|
||||||
import UnknownInstruction from './instructions/UnknownInstruction';
|
import UnknownInstruction from './instructions/UnknownInstruction';
|
||||||
|
import StakeInstruction from '../components/instructions/StakeInstruction';
|
||||||
import SystemInstruction from '../components/instructions/SystemInstruction';
|
import SystemInstruction from '../components/instructions/SystemInstruction';
|
||||||
import DexInstruction from '../components/instructions/DexInstruction';
|
import DexInstruction from '../components/instructions/DexInstruction';
|
||||||
import TokenInstruction from '../components/instructions/TokenInstruction';
|
import TokenInstruction from '../components/instructions/TokenInstruction';
|
||||||
|
@ -215,6 +216,19 @@ export default function SignTransactionFormContent({
|
||||||
onOpenAddress={onOpenAddress}
|
onOpenAddress={onOpenAddress}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
case 'stakeAuthorizeWithSeed':
|
||||||
|
case 'stakeAuthorize':
|
||||||
|
case 'stakeDeactivate':
|
||||||
|
case 'stakeDelegate':
|
||||||
|
case 'stakeInitialize':
|
||||||
|
case 'stakeSplit':
|
||||||
|
case 'stakeWithdraw':
|
||||||
|
return (
|
||||||
|
<StakeInstruction
|
||||||
|
instruction={instruction}
|
||||||
|
onOpenAddress={onOpenAddress}
|
||||||
|
/>
|
||||||
|
);
|
||||||
case 'newOrder':
|
case 'newOrder':
|
||||||
return (
|
return (
|
||||||
<NewOrder instruction={instruction} onOpenAddress={onOpenAddress} />
|
<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 bs58 from 'bs58';
|
||||||
import { Message, SystemInstruction, SystemProgram } from '@solana/web3.js';
|
import { Message, StakeInstruction, StakeProgram, SystemInstruction, SystemProgram } from '@solana/web3.js';
|
||||||
import {
|
import {
|
||||||
decodeInstruction,
|
decodeInstruction,
|
||||||
decodeTokenInstructionData,
|
decodeTokenInstructionData,
|
||||||
|
@ -96,6 +96,9 @@ const toInstruction = async (
|
||||||
if (programId.equals(SystemProgram.programId)) {
|
if (programId.equals(SystemProgram.programId)) {
|
||||||
console.log('[' + index + '] Handled as system instruction');
|
console.log('[' + index + '] Handled as system instruction');
|
||||||
return handleSystemInstruction(publicKey, instruction, accountKeys);
|
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)) {
|
} else if (programId.equals(TOKEN_PROGRAM_ID)) {
|
||||||
console.log('[' + index + '] Handled as token instruction');
|
console.log('[' + index + '] Handled as token instruction');
|
||||||
let decodedInstruction = decodeTokenInstruction(decoded);
|
let decodedInstruction = decodeTokenInstruction(decoded);
|
||||||
|
@ -155,7 +158,9 @@ const toInstruction = async (
|
||||||
programId,
|
programId,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} catch {}
|
} catch (e) {
|
||||||
|
console.log(`Failed to decode instruction: ${e}`);
|
||||||
|
}
|
||||||
|
|
||||||
// all decodings failed
|
// all decodings failed
|
||||||
console.log('[' + index + '] Failed, data: ' + JSON.stringify(decoded));
|
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 = (
|
const handleTokenInstruction = (
|
||||||
publicKey,
|
publicKey,
|
||||||
accounts,
|
accounts,
|
||||||
|
|
Loading…
Reference in New Issue