Add support for parsed stake accounts (#11469)
This commit is contained in:
parent
102d15f081
commit
c544116cf2
|
@ -6,4 +6,5 @@ branches:
|
||||||
- master
|
- master
|
||||||
script:
|
script:
|
||||||
- npm run build
|
- npm run build
|
||||||
|
- npm run test
|
||||||
- npm run format
|
- npm run format
|
||||||
|
|
|
@ -2843,6 +2843,14 @@
|
||||||
"@babel/types": "^7.3.0"
|
"@babel/types": "^7.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/bn.js": {
|
||||||
|
"version": "4.11.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz",
|
||||||
|
"integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==",
|
||||||
|
"requires": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/bs58": {
|
"@types/bs58": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/bs58/-/bs58-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/bs58/-/bs58-4.0.1.tgz",
|
||||||
|
@ -2851,6 +2859,11 @@
|
||||||
"base-x": "^3.0.6"
|
"base-x": "^3.0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/chai": {
|
||||||
|
"version": "4.2.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.12.tgz",
|
||||||
|
"integrity": "sha512-aN5IAC8QNtSUdQzxu7lGBgYAOuU1tmRU4c9dIq5OKGf/SBVjXo+ffM2wEjudAWbgpOhy60nLoAGH1xm8fpCKFQ=="
|
||||||
|
},
|
||||||
"@types/color-name": {
|
"@types/color-name": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
|
||||||
|
@ -3791,6 +3804,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
|
||||||
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
|
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
|
||||||
},
|
},
|
||||||
|
"assertion-error": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw=="
|
||||||
|
},
|
||||||
"assign-symbols": {
|
"assign-symbols": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
|
||||||
|
@ -5087,6 +5105,34 @@
|
||||||
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
|
||||||
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
|
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
|
||||||
},
|
},
|
||||||
|
"chai": {
|
||||||
|
"version": "4.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz",
|
||||||
|
"integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==",
|
||||||
|
"requires": {
|
||||||
|
"assertion-error": "^1.1.0",
|
||||||
|
"check-error": "^1.0.2",
|
||||||
|
"deep-eql": "^3.0.1",
|
||||||
|
"get-func-name": "^2.0.0",
|
||||||
|
"pathval": "^1.1.0",
|
||||||
|
"type-detect": "^4.0.5"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"deep-eql": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==",
|
||||||
|
"requires": {
|
||||||
|
"type-detect": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type-detect": {
|
||||||
|
"version": "4.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
|
||||||
|
"integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"chalk": {
|
"chalk": {
|
||||||
"version": "2.4.2",
|
"version": "2.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||||
|
@ -5102,6 +5148,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
|
||||||
"integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA=="
|
"integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA=="
|
||||||
},
|
},
|
||||||
|
"check-error": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII="
|
||||||
|
},
|
||||||
"cheerio": {
|
"cheerio": {
|
||||||
"version": "0.22.0",
|
"version": "0.22.0",
|
||||||
"resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz",
|
"resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz",
|
||||||
|
@ -7882,6 +7933,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
|
||||||
"integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w=="
|
"integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w=="
|
||||||
},
|
},
|
||||||
|
"get-func-name": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE="
|
||||||
|
},
|
||||||
"get-own-enumerable-property-symbols": {
|
"get-own-enumerable-property-symbols": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz",
|
||||||
|
@ -11737,6 +11793,11 @@
|
||||||
"pinkie-promise": "^2.0.0"
|
"pinkie-promise": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"pathval": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz",
|
||||||
|
"integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA="
|
||||||
|
},
|
||||||
"pbkdf2": {
|
"pbkdf2": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz",
|
||||||
|
|
|
@ -8,7 +8,9 @@
|
||||||
"@testing-library/jest-dom": "^5.11.2",
|
"@testing-library/jest-dom": "^5.11.2",
|
||||||
"@testing-library/react": "^10.4.8",
|
"@testing-library/react": "^10.4.8",
|
||||||
"@testing-library/user-event": "^12.1.0",
|
"@testing-library/user-event": "^12.1.0",
|
||||||
|
"@types/bn.js": "^4.11.6",
|
||||||
"@types/bs58": "^4.0.1",
|
"@types/bs58": "^4.0.1",
|
||||||
|
"@types/chai": "^4.2.12",
|
||||||
"@types/jest": "^26.0.9",
|
"@types/jest": "^26.0.9",
|
||||||
"@types/node": "^14.0.27",
|
"@types/node": "^14.0.27",
|
||||||
"@types/react": "^16.9.44",
|
"@types/react": "^16.9.44",
|
||||||
|
@ -16,8 +18,10 @@
|
||||||
"@types/react-router-dom": "^5.1.5",
|
"@types/react-router-dom": "^5.1.5",
|
||||||
"@types/react-select": "^3.0.16",
|
"@types/react-select": "^3.0.16",
|
||||||
"@types/socket.io-client": "^1.4.33",
|
"@types/socket.io-client": "^1.4.33",
|
||||||
|
"bn.js": "^5.1.2",
|
||||||
"bootstrap": "^4.5.1",
|
"bootstrap": "^4.5.1",
|
||||||
"bs58": "^4.0.1",
|
"bs58": "^4.0.1",
|
||||||
|
"chai": "^4.2.0",
|
||||||
"humanize-duration-ts": "^2.1.1",
|
"humanize-duration-ts": "^2.1.1",
|
||||||
"node-sass": "^4.14.1",
|
"node-sass": "^4.14.1",
|
||||||
"prettier": "^2.0.5",
|
"prettier": "^2.0.5",
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { expect } from "chai";
|
||||||
|
import { lamportsToSol, LAMPORTS_PER_SOL } from "utils";
|
||||||
|
import BN from "bn.js";
|
||||||
|
|
||||||
|
describe("lamportsToSol", () => {
|
||||||
|
it("0 lamports", () => {
|
||||||
|
expect(lamportsToSol(new BN(0))).to.eq(0.0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("1 lamport", () => {
|
||||||
|
expect(lamportsToSol(new BN(1))).to.eq(0.000000001);
|
||||||
|
expect(lamportsToSol(new BN(-1))).to.eq(-0.000000001);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("1 SOL", () => {
|
||||||
|
expect(lamportsToSol(new BN(LAMPORTS_PER_SOL))).to.eq(1.0);
|
||||||
|
expect(lamportsToSol(new BN(-LAMPORTS_PER_SOL))).to.eq(-1.0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("u64::MAX lamports", () => {
|
||||||
|
expect(lamportsToSol(new BN(2).pow(new BN(64)))).to.eq(
|
||||||
|
18446744073.709551615
|
||||||
|
);
|
||||||
|
expect(lamportsToSol(new BN(2).pow(new BN(64)).neg())).to.eq(
|
||||||
|
-18446744073.709551615
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,29 +1,54 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { StakeAccount, Meta } from "solana-sdk-wasm";
|
import { StakeAccount as StakeAccountWasm, Meta } from "solana-sdk-wasm";
|
||||||
import { TableCardBody } from "components/common/TableCardBody";
|
import { TableCardBody } from "components/common/TableCardBody";
|
||||||
import { lamportsToSolString } from "utils";
|
import { lamportsToSolString } from "utils";
|
||||||
import { displayTimestamp } from "utils/date";
|
import { displayTimestamp } from "utils/date";
|
||||||
import { Account, useFetchAccountInfo } from "providers/accounts";
|
import { Account, useFetchAccountInfo } from "providers/accounts";
|
||||||
import { Address } from "components/common/Address";
|
import { Address } from "components/common/Address";
|
||||||
|
import {
|
||||||
|
StakeAccountInfo,
|
||||||
|
StakeMeta,
|
||||||
|
StakeAccountType,
|
||||||
|
} from "providers/accounts/types";
|
||||||
|
import BN from "bn.js";
|
||||||
|
|
||||||
|
const MAX_EPOCH = new BN(2).pow(new BN(64));
|
||||||
|
|
||||||
export function StakeAccountSection({
|
export function StakeAccountSection({
|
||||||
account,
|
account,
|
||||||
stakeAccount,
|
stakeAccount,
|
||||||
|
stakeAccountType,
|
||||||
}: {
|
}: {
|
||||||
account: Account;
|
account: Account;
|
||||||
stakeAccount: StakeAccount;
|
stakeAccount: StakeAccountInfo | StakeAccountWasm;
|
||||||
|
stakeAccountType: StakeAccountType;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<LockupCard stakeAccount={stakeAccount} />
|
<LockupCard stakeAccount={stakeAccount} />
|
||||||
<OverviewCard account={account} stakeAccount={stakeAccount} />
|
<OverviewCard
|
||||||
{stakeAccount.meta && <DelegationCard stakeAccount={stakeAccount} />}
|
account={account}
|
||||||
{stakeAccount.meta && <AuthoritiesCard meta={stakeAccount.meta} />}
|
stakeAccount={stakeAccount}
|
||||||
|
stakeAccountType={stakeAccountType}
|
||||||
|
/>
|
||||||
|
{stakeAccount.meta && (
|
||||||
|
<>
|
||||||
|
<DelegationCard
|
||||||
|
stakeAccount={stakeAccount}
|
||||||
|
stakeAccountType={stakeAccountType}
|
||||||
|
/>
|
||||||
|
<AuthoritiesCard meta={stakeAccount.meta} />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function LockupCard({ stakeAccount }: { stakeAccount: StakeAccount }) {
|
function LockupCard({
|
||||||
|
stakeAccount,
|
||||||
|
}: {
|
||||||
|
stakeAccount: StakeAccountInfo | StakeAccountWasm;
|
||||||
|
}) {
|
||||||
const unixTimestamp = stakeAccount.meta?.lockup.unixTimestamp;
|
const unixTimestamp = stakeAccount.meta?.lockup.unixTimestamp;
|
||||||
if (unixTimestamp && unixTimestamp > 0) {
|
if (unixTimestamp && unixTimestamp > 0) {
|
||||||
const prettyTimestamp = displayTimestamp(unixTimestamp * 1000);
|
const prettyTimestamp = displayTimestamp(unixTimestamp * 1000);
|
||||||
|
@ -37,12 +62,21 @@ function LockupCard({ stakeAccount }: { stakeAccount: StakeAccount }) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const TYPE_NAMES = {
|
||||||
|
uninitialized: "Uninitialized",
|
||||||
|
initialized: "Initialized",
|
||||||
|
delegated: "Delegated",
|
||||||
|
rewardsPool: "RewardsPool",
|
||||||
|
};
|
||||||
|
|
||||||
function OverviewCard({
|
function OverviewCard({
|
||||||
account,
|
account,
|
||||||
stakeAccount,
|
stakeAccount,
|
||||||
|
stakeAccountType,
|
||||||
}: {
|
}: {
|
||||||
account: Account;
|
account: Account;
|
||||||
stakeAccount: StakeAccount;
|
stakeAccount: StakeAccountInfo | StakeAccountWasm;
|
||||||
|
stakeAccountType: StakeAccountType;
|
||||||
}) {
|
}) {
|
||||||
const refresh = useFetchAccountInfo();
|
const refresh = useFetchAccountInfo();
|
||||||
return (
|
return (
|
||||||
|
@ -84,7 +118,7 @@ function OverviewCard({
|
||||||
{!stakeAccount.meta && (
|
{!stakeAccount.meta && (
|
||||||
<tr>
|
<tr>
|
||||||
<td>State</td>
|
<td>State</td>
|
||||||
<td className="text-lg-right">{stakeAccount.displayState()}</td>
|
<td className="text-lg-right">{TYPE_NAMES[stakeAccountType]}</td>
|
||||||
</tr>
|
</tr>
|
||||||
)}
|
)}
|
||||||
</TableCardBody>
|
</TableCardBody>
|
||||||
|
@ -92,16 +126,48 @@ function OverviewCard({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function DelegationCard({ stakeAccount }: { stakeAccount: StakeAccount }) {
|
function DelegationCard({
|
||||||
const { stake } = stakeAccount;
|
stakeAccount,
|
||||||
|
stakeAccountType,
|
||||||
|
}: {
|
||||||
|
stakeAccount: StakeAccountInfo | StakeAccountWasm;
|
||||||
|
stakeAccountType: StakeAccountType;
|
||||||
|
}) {
|
||||||
const displayStatus = () => {
|
const displayStatus = () => {
|
||||||
let status = stakeAccount.displayState();
|
// TODO check epoch
|
||||||
if (status !== "Delegated") {
|
let status = TYPE_NAMES[stakeAccountType];
|
||||||
|
if (stakeAccountType !== "delegated") {
|
||||||
status = "Not delegated";
|
status = "Not delegated";
|
||||||
}
|
}
|
||||||
return status;
|
return status;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let voterPubkey, activationEpoch, deactivationEpoch;
|
||||||
|
if ("accountType" in stakeAccount) {
|
||||||
|
const delegation = stakeAccount?.stake?.delegation;
|
||||||
|
if (delegation) {
|
||||||
|
voterPubkey = delegation.voterPubkey;
|
||||||
|
activationEpoch = delegation.isBootstrapStake()
|
||||||
|
? "-"
|
||||||
|
: delegation.activationEpoch;
|
||||||
|
deactivationEpoch = delegation.isDeactivated()
|
||||||
|
? delegation.deactivationEpoch
|
||||||
|
: "-";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const delegation = stakeAccount?.stake?.delegation;
|
||||||
|
if (delegation) {
|
||||||
|
voterPubkey = delegation.voter;
|
||||||
|
activationEpoch = delegation.activationEpoch.eq(MAX_EPOCH)
|
||||||
|
? "-"
|
||||||
|
: delegation.activationEpoch.toString();
|
||||||
|
deactivationEpoch = delegation.deactivationEpoch.eq(MAX_EPOCH)
|
||||||
|
? "-"
|
||||||
|
: delegation.deactivationEpoch.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const { stake } = stakeAccount;
|
||||||
return (
|
return (
|
||||||
<div className="card">
|
<div className="card">
|
||||||
<div className="card-header">
|
<div className="card-header">
|
||||||
|
@ -124,33 +190,23 @@ function DelegationCard({ stakeAccount }: { stakeAccount: StakeAccount }) {
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
{voterPubkey && (
|
||||||
<td>Delegated Vote Address</td>
|
<tr>
|
||||||
<td className="text-lg-right">
|
<td>Delegated Vote Address</td>
|
||||||
<Address
|
<td className="text-lg-right">
|
||||||
pubkey={stake.delegation.voterPubkey}
|
<Address pubkey={voterPubkey} alignRight link />
|
||||||
alignRight
|
</td>
|
||||||
link
|
</tr>
|
||||||
/>
|
)}
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>Activation Epoch</td>
|
<td>Activation Epoch</td>
|
||||||
<td className="text-lg-right">
|
<td className="text-lg-right">{activationEpoch}</td>
|
||||||
{stake.delegation.isBootstrapStake()
|
|
||||||
? "-"
|
|
||||||
: stake.delegation.activationEpoch}
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>Deactivation Epoch</td>
|
<td>Deactivation Epoch</td>
|
||||||
<td className="text-lg-right">
|
<td className="text-lg-right">{deactivationEpoch}</td>
|
||||||
{stake.delegation.isDeactivated()
|
|
||||||
? stake.delegation.deactivationEpoch
|
|
||||||
: "-"}
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
@ -159,7 +215,7 @@ function DelegationCard({ stakeAccount }: { stakeAccount: StakeAccount }) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function AuthoritiesCard({ meta }: { meta: Meta }) {
|
function AuthoritiesCard({ meta }: { meta: Meta | StakeMeta }) {
|
||||||
const hasLockup = meta && meta.lockup.unixTimestamp > 0;
|
const hasLockup = meta && meta.lockup.unixTimestamp > 0;
|
||||||
return (
|
return (
|
||||||
<div className="card">
|
<div className="card">
|
||||||
|
|
|
@ -28,7 +28,7 @@ export function UnknownAccountCard({ account }: { account: Account }) {
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
{details && (
|
{details?.space !== undefined && (
|
||||||
<tr>
|
<tr>
|
||||||
<td>Data (Bytes)</td>
|
<td>Data (Bytes)</td>
|
||||||
<td className="text-lg-right">{details.space}</td>
|
<td className="text-lg-right">{details.space}</td>
|
||||||
|
|
|
@ -10,7 +10,8 @@ import {
|
||||||
import { UnknownDetailsCard } from "../UnknownDetailsCard";
|
import { UnknownDetailsCard } from "../UnknownDetailsCard";
|
||||||
import { InstructionCard } from "../InstructionCard";
|
import { InstructionCard } from "../InstructionCard";
|
||||||
import { Address } from "components/common/Address";
|
import { Address } from "components/common/Address";
|
||||||
import { ParsedInstructionInfo, IX_STRUCTS } from "./types";
|
import { IX_STRUCTS, TokenInstructionType } from "./types";
|
||||||
|
import { ParsedInfo } from "validators";
|
||||||
|
|
||||||
const IX_TITLES = {
|
const IX_TITLES = {
|
||||||
initializeMint: "Initialize Mint",
|
initializeMint: "Initialize Mint",
|
||||||
|
@ -34,8 +35,9 @@ type DetailsProps = {
|
||||||
|
|
||||||
export function TokenDetailsCard(props: DetailsProps) {
|
export function TokenDetailsCard(props: DetailsProps) {
|
||||||
try {
|
try {
|
||||||
const parsed = coerce(props.ix.parsed, ParsedInstructionInfo);
|
const parsed = coerce(props.ix.parsed, ParsedInfo);
|
||||||
const { type, info } = parsed;
|
const { type: rawType, info } = parsed;
|
||||||
|
const type = coerce(rawType, TokenInstructionType);
|
||||||
const title = `Token: ${IX_TITLES[type]}`;
|
const title = `Token: ${IX_TITLES[type]}`;
|
||||||
const coerced = coerce(info, IX_STRUCTS[type] as any);
|
const coerced = coerce(info, IX_STRUCTS[type] as any);
|
||||||
return <TokenInstruction title={title} info={coerced} {...props} />;
|
return <TokenInstruction title={title} info={coerced} {...props} />;
|
||||||
|
|
|
@ -1,21 +1,12 @@
|
||||||
import {
|
import {
|
||||||
enums,
|
enums,
|
||||||
object,
|
object,
|
||||||
any,
|
|
||||||
StructType,
|
StructType,
|
||||||
coercion,
|
|
||||||
struct,
|
|
||||||
number,
|
number,
|
||||||
optional,
|
optional,
|
||||||
array,
|
array,
|
||||||
} from "superstruct";
|
} from "superstruct";
|
||||||
import { PublicKey } from "@solana/web3.js";
|
import { Pubkey } from "validators/pubkey";
|
||||||
|
|
||||||
const PubkeyValue = struct("Pubkey", (value) => value instanceof PublicKey);
|
|
||||||
const Pubkey = coercion(PubkeyValue, (value) => {
|
|
||||||
if (typeof value === "string") return new PublicKey(value);
|
|
||||||
throw new Error("invalid pubkey");
|
|
||||||
});
|
|
||||||
|
|
||||||
const InitializeMint = object({
|
const InitializeMint = object({
|
||||||
mint: Pubkey,
|
mint: Pubkey,
|
||||||
|
@ -95,8 +86,8 @@ const CloseAccount = object({
|
||||||
signers: optional(array(Pubkey)),
|
signers: optional(array(Pubkey)),
|
||||||
});
|
});
|
||||||
|
|
||||||
type TokenInstructionType = StructType<typeof TokenInstructionType>;
|
export type TokenInstructionType = StructType<typeof TokenInstructionType>;
|
||||||
const TokenInstructionType = enums([
|
export const TokenInstructionType = enums([
|
||||||
"initializeMint",
|
"initializeMint",
|
||||||
"initializeAccount",
|
"initializeAccount",
|
||||||
"initializeMultisig",
|
"initializeMultisig",
|
||||||
|
@ -121,9 +112,3 @@ export const IX_STRUCTS = {
|
||||||
burn: Burn,
|
burn: Burn,
|
||||||
closeAccount: CloseAccount,
|
closeAccount: CloseAccount,
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ParsedInstructionInfo = StructType<typeof ParsedInstructionInfo>;
|
|
||||||
export const ParsedInstructionInfo = object({
|
|
||||||
type: TokenInstructionType,
|
|
||||||
info: any(),
|
|
||||||
});
|
|
||||||
|
|
|
@ -68,7 +68,22 @@ function InfoSection({ pubkey }: { pubkey: PublicKey }) {
|
||||||
const owner = info.details?.owner;
|
const owner = info.details?.owner;
|
||||||
const data = info.details?.data;
|
const data = info.details?.data;
|
||||||
if (data && owner && owner.equals(StakeProgram.programId)) {
|
if (data && owner && owner.equals(StakeProgram.programId)) {
|
||||||
return <StakeAccountSection account={info} stakeAccount={data} />;
|
let stakeAccountType, stakeAccount;
|
||||||
|
if ("accountType" in data) {
|
||||||
|
stakeAccount = data;
|
||||||
|
stakeAccountType = data.accountType as any;
|
||||||
|
} else {
|
||||||
|
stakeAccount = data.info;
|
||||||
|
stakeAccountType = data.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StakeAccountSection
|
||||||
|
account={info}
|
||||||
|
stakeAccount={stakeAccount}
|
||||||
|
stakeAccountType={stakeAccountType}
|
||||||
|
/>
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
return <UnknownAccountCard account={info} />;
|
return <UnknownAccountCard account={info} />;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { StakeAccount } from "solana-sdk-wasm";
|
import { StakeAccount as StakeAccountWasm } from "solana-sdk-wasm";
|
||||||
import { PublicKey, Connection, StakeProgram } from "@solana/web3.js";
|
import { PublicKey, Connection, StakeProgram } from "@solana/web3.js";
|
||||||
import { useCluster } from "../cluster";
|
import { useCluster } from "../cluster";
|
||||||
import { HistoryProvider } from "./history";
|
import { HistoryProvider } from "./history";
|
||||||
import { TokensProvider } from "./tokens";
|
import { TokensProvider } from "./tokens";
|
||||||
|
import { coerce } from "superstruct";
|
||||||
|
import { ParsedInfo } from "validators";
|
||||||
|
import { StakeAccount } from "./types";
|
||||||
export { useAccountHistory } from "./history";
|
export { useAccountHistory } from "./history";
|
||||||
|
|
||||||
export enum FetchStatus {
|
export enum FetchStatus {
|
||||||
|
@ -15,8 +18,8 @@ export enum FetchStatus {
|
||||||
export interface Details {
|
export interface Details {
|
||||||
executable: boolean;
|
executable: boolean;
|
||||||
owner: PublicKey;
|
owner: PublicKey;
|
||||||
space: number;
|
space?: number;
|
||||||
data?: StakeAccount;
|
data?: StakeAccount | StakeAccountWasm;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Account {
|
export interface Account {
|
||||||
|
@ -156,18 +159,30 @@ async function fetchAccountInfo(
|
||||||
let details;
|
let details;
|
||||||
let lamports;
|
let lamports;
|
||||||
try {
|
try {
|
||||||
const result = await new Connection(url, "recent").getAccountInfo(pubkey);
|
const result = (
|
||||||
|
await new Connection(url, "single").getParsedAccountInfo(pubkey)
|
||||||
|
).value;
|
||||||
if (result === null) {
|
if (result === null) {
|
||||||
lamports = 0;
|
lamports = 0;
|
||||||
} else {
|
} else {
|
||||||
lamports = result.lamports;
|
lamports = result.lamports;
|
||||||
let data = undefined;
|
|
||||||
|
|
||||||
// Only save data in memory if we can decode it
|
// Only save data in memory if we can decode it
|
||||||
|
let space;
|
||||||
|
if (!("parsed" in result.data)) {
|
||||||
|
space = result.data.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
let data;
|
||||||
if (result.owner.equals(StakeProgram.programId)) {
|
if (result.owner.equals(StakeProgram.programId)) {
|
||||||
try {
|
try {
|
||||||
const wasm = await import("solana-sdk-wasm");
|
if ("parsed" in result.data) {
|
||||||
data = wasm.StakeAccount.fromAccountData(result.data);
|
const info = coerce(result.data.parsed, ParsedInfo);
|
||||||
|
data = coerce(info, StakeAccount);
|
||||||
|
} else {
|
||||||
|
const wasm = await import("solana-sdk-wasm");
|
||||||
|
data = wasm.StakeAccount.fromAccountData(result.data);
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Unexpected error loading wasm", err);
|
console.error("Unexpected error loading wasm", err);
|
||||||
// TODO store error state in Account info
|
// TODO store error state in Account info
|
||||||
|
@ -175,7 +190,7 @@ async function fetchAccountInfo(
|
||||||
}
|
}
|
||||||
|
|
||||||
details = {
|
details = {
|
||||||
space: result.data.length,
|
space,
|
||||||
executable: result.executable,
|
executable: result.executable,
|
||||||
owner: result.owner,
|
owner: result.owner,
|
||||||
data,
|
data,
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
import { object, StructType, number, optional, enums } from "superstruct";
|
||||||
|
import { Pubkey } from "validators/pubkey";
|
||||||
|
import { BigNum } from "validators/bignum";
|
||||||
|
|
||||||
|
export type StakeAccountType = StructType<typeof StakeAccountType>;
|
||||||
|
export const StakeAccountType = enums([
|
||||||
|
"uninitialized",
|
||||||
|
"initialized",
|
||||||
|
"delegated",
|
||||||
|
"rewardsPool",
|
||||||
|
]);
|
||||||
|
|
||||||
|
export type StakeMeta = StructType<typeof StakeMeta>;
|
||||||
|
export const StakeMeta = object({
|
||||||
|
rentExemptReserve: BigNum,
|
||||||
|
authorized: object({
|
||||||
|
staker: Pubkey,
|
||||||
|
withdrawer: Pubkey,
|
||||||
|
}),
|
||||||
|
lockup: object({
|
||||||
|
unixTimestamp: number(),
|
||||||
|
epoch: number(),
|
||||||
|
custodian: Pubkey,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type StakeAccountInfo = StructType<typeof StakeAccountInfo>;
|
||||||
|
export const StakeAccountInfo = object({
|
||||||
|
meta: StakeMeta,
|
||||||
|
stake: optional(
|
||||||
|
object({
|
||||||
|
delegation: object({
|
||||||
|
voter: Pubkey,
|
||||||
|
stake: BigNum,
|
||||||
|
activationEpoch: BigNum,
|
||||||
|
deactivationEpoch: BigNum,
|
||||||
|
warmupCooldownRate: number(),
|
||||||
|
}),
|
||||||
|
creditsObserved: number(),
|
||||||
|
})
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type StakeAccount = StructType<typeof StakeAccount>;
|
||||||
|
export const StakeAccount = object({
|
||||||
|
type: StakeAccountType,
|
||||||
|
info: StakeAccountInfo,
|
||||||
|
});
|
|
@ -1,10 +1,12 @@
|
||||||
import React from "react";
|
import React, { ReactNode } from "react";
|
||||||
import { LAMPORTS_PER_SOL } from "@solana/web3.js";
|
import BN from "bn.js";
|
||||||
import {
|
import {
|
||||||
HumanizeDuration,
|
HumanizeDuration,
|
||||||
HumanizeDurationLanguage,
|
HumanizeDurationLanguage,
|
||||||
} from "humanize-duration-ts";
|
} from "humanize-duration-ts";
|
||||||
import { ReactNode } from "react";
|
|
||||||
|
// Switch to web3 constant when web3 updates superstruct
|
||||||
|
export const LAMPORTS_PER_SOL = 1000000000;
|
||||||
|
|
||||||
export const NUM_TICKS_PER_SECOND = 160;
|
export const NUM_TICKS_PER_SECOND = 160;
|
||||||
export const DEFAULT_TICKS_PER_SLOT = 64;
|
export const DEFAULT_TICKS_PER_SLOT = 64;
|
||||||
|
@ -16,11 +18,31 @@ export function assertUnreachable(x: never): never {
|
||||||
throw new Error("Unreachable!");
|
throw new Error("Unreachable!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function lamportsToSol(lamports: number | BN): number {
|
||||||
|
if (typeof lamports === "number") {
|
||||||
|
return Math.abs(lamports) / LAMPORTS_PER_SOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
let signMultiplier = 1;
|
||||||
|
if (lamports.isNeg()) {
|
||||||
|
signMultiplier = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const absLamports = lamports.abs();
|
||||||
|
const lamportsString = absLamports.toString(10).padStart(10, "0");
|
||||||
|
const splitIndex = lamportsString.length - 9;
|
||||||
|
const solString =
|
||||||
|
lamportsString.slice(0, splitIndex) +
|
||||||
|
"." +
|
||||||
|
lamportsString.slice(splitIndex);
|
||||||
|
return signMultiplier * parseFloat(solString);
|
||||||
|
}
|
||||||
|
|
||||||
export function lamportsToSolString(
|
export function lamportsToSolString(
|
||||||
lamports: number,
|
lamports: number | BN,
|
||||||
maximumFractionDigits: number = 9
|
maximumFractionDigits: number = 9
|
||||||
): ReactNode {
|
): ReactNode {
|
||||||
const sol = Math.abs(lamports) / LAMPORTS_PER_SOL;
|
const sol = lamportsToSol(lamports);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
◎
|
◎
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { coercion, struct, Struct } from "superstruct";
|
||||||
|
import BN from "bn.js";
|
||||||
|
|
||||||
|
const BigNumValue = struct("BigNum", (value) => value instanceof BN);
|
||||||
|
export const BigNum: Struct<BN, any> = coercion(BigNumValue, (value) => {
|
||||||
|
if (typeof value === "string") return new BN(value, 10);
|
||||||
|
throw new Error("invalid big num");
|
||||||
|
});
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { object, any, StructType, string } from "superstruct";
|
||||||
|
|
||||||
|
export type ParsedInfo = StructType<typeof ParsedInfo>;
|
||||||
|
export const ParsedInfo = object({
|
||||||
|
type: string(),
|
||||||
|
info: any(),
|
||||||
|
});
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { coercion, struct, Struct } from "superstruct";
|
||||||
|
import { PublicKey } from "@solana/web3.js";
|
||||||
|
|
||||||
|
const PubkeyValue = struct("Pubkey", (value) => value instanceof PublicKey);
|
||||||
|
export const Pubkey: Struct<PublicKey, any> = coercion(PubkeyValue, (value) => {
|
||||||
|
if (typeof value === "string") return new PublicKey(value);
|
||||||
|
throw new Error("invalid pubkey");
|
||||||
|
});
|
|
@ -4,7 +4,7 @@
|
||||||
"Solana Maintainers <maintainers@solana.com>"
|
"Solana Maintainers <maintainers@solana.com>"
|
||||||
],
|
],
|
||||||
"description": "Solana SDK Wasm",
|
"description": "Solana SDK Wasm",
|
||||||
"version": "1.2.0",
|
"version": "1.4.0",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|
|
@ -124,7 +124,7 @@ export class StakeAccount {
|
||||||
/**
|
/**
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
displayState(): string;
|
accountType(): string;
|
||||||
/**
|
/**
|
||||||
* @returns {Meta | undefined}
|
* @returns {Meta | undefined}
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -9,7 +9,7 @@ export function __wbg_set_stakeaccount_stake(a: number, b: number): void;
|
||||||
export function __wbg_get_stakeaccount_state(a: number): number;
|
export function __wbg_get_stakeaccount_state(a: number): number;
|
||||||
export function __wbg_set_stakeaccount_state(a: number, b: number): void;
|
export function __wbg_set_stakeaccount_state(a: number, b: number): void;
|
||||||
export function stakeaccount_fromAccountData(a: number, b: number): number;
|
export function stakeaccount_fromAccountData(a: number, b: number): number;
|
||||||
export function stakeaccount_displayState(a: number, b: number): void;
|
export function stakeaccount_accountType(a: number, b: number): void;
|
||||||
export function __wbg_lockup_free(a: number): void;
|
export function __wbg_lockup_free(a: number): void;
|
||||||
export function __wbg_get_lockup_custodian(a: number): number;
|
export function __wbg_get_lockup_custodian(a: number): number;
|
||||||
export function __wbg_set_lockup_custodian(a: number, b: number): void;
|
export function __wbg_set_lockup_custodian(a: number, b: number): void;
|
||||||
|
|
|
@ -456,9 +456,9 @@ export class StakeAccount {
|
||||||
/**
|
/**
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
displayState() {
|
accountType() {
|
||||||
try {
|
try {
|
||||||
wasm.stakeaccount_displayState(8, this.ptr);
|
wasm.stakeaccount_accountType(8, this.ptr);
|
||||||
var r0 = getInt32Memory0()[8 / 4 + 0];
|
var r0 = getInt32Memory0()[8 / 4 + 0];
|
||||||
var r1 = getInt32Memory0()[8 / 4 + 1];
|
var r1 = getInt32Memory0()[8 / 4 + 1];
|
||||||
return getStringFromWasm0(r0, r1);
|
return getStringFromWasm0(r0, r1);
|
||||||
|
|
Binary file not shown.
|
@ -62,13 +62,13 @@ impl StakeAccount {
|
||||||
return Ok(stake_state.into());
|
return Ok(stake_state.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen(js_name = displayState)]
|
#[wasm_bindgen(js_name = accountType)]
|
||||||
pub fn display_state(&self) -> String {
|
pub fn account_type(&self) -> String {
|
||||||
match self.state {
|
match self.state {
|
||||||
State::Uninitialized => "Uninitialized".to_string(),
|
State::Uninitialized => "uninitialized".to_string(),
|
||||||
State::Initialized => "Initialized".to_string(),
|
State::Initialized => "initialized".to_string(),
|
||||||
State::Delegated => "Delegated".to_string(),
|
State::Delegated => "delegated".to_string(),
|
||||||
State::RewardsPool => "RewardsPool".to_string(),
|
State::RewardsPool => "rewardsPool".to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue