@j0nnyboi/amman
Version:
A modern mandatory toolbelt to help test solana SDK libraries and apps on a locally running validator.
231 lines • 8.63 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.printableAccount = exports.AccountStates = void 0;
const amman_client_1 = require("@j0nnyboi/amman-client");
const web3_js_1 = require("@safecoin/web3.js");
const assert_1 = require("assert");
const bn_js_1 = __importDefault(require("bn.js"));
const deep_diff_1 = require("deep-diff");
const Diff = __importStar(require("diff"));
const events_1 = __importDefault(require("events"));
const log_1 = require("../utils/log");
class AccountStateTracker {
constructor() {
this.states = [];
}
add(state) {
const lastState = this.states.length > 0 ? this.states[this.states.length - 1] : null;
const accountDiff = lastState == null || lastState.account == null || state.account == null
? undefined
: (0, deep_diff_1.diff)(lastState.account.pretty(), state.account.pretty());
const processedState = {
...state,
accountDiff,
timestamp: Date.now().valueOf(),
};
const renderedDiff = this.renderDiff(lastState, state);
if (renderedDiff != null) {
processedState.renderedDiff = renderedDiff;
}
this.states.push(processedState);
}
get relayStates() {
return this.states
.filter((state) => state.account != null)
.map(({ account, data, ...rest }) => ({
account: account.pretty(),
...rest,
}));
}
renderDiff(lastState, state) {
if ((lastState === null || lastState === void 0 ? void 0 : lastState.rendered) == null)
return undefined;
if (state.rendered == null)
return undefined;
return Diff.diffChars(lastState.rendered, state.rendered);
}
accountStateForSlot(slot) {
return this.states.find((state) => state.slot === slot);
}
accountDataForSlot(slot) {
var _a;
return (_a = this.accountStateForSlot(slot)) === null || _a === void 0 ? void 0 : _a.data;
}
}
class AccountStates extends events_1.default {
constructor(connection, accountProvider, loadedAccountInfos,
// label:Keypair
loadedKeypairs) {
super();
this.connection = connection;
this.accountProvider = accountProvider;
this.loadedAccountInfos = loadedAccountInfos;
this.loadedKeypairs = loadedKeypairs;
this.states = new Map();
this.keypairs = new Map();
this._paused = false;
this._onLog = async (logs, ctx) => {
if (this._paused)
return;
const tx = await this.connection.getTransaction(logs.signature, {
commitment: 'confirmed',
});
if (tx == null) {
(0, log_1.logDebug)(`Could not find transaction ${logs.signature}`);
return;
}
const nonProgramAddresses = tx.transaction.message
.nonProgramIds()
.map((x) => x.toBase58());
for (const key of nonProgramAddresses) {
if (this._paused)
return;
this.update(key, ctx.slot);
}
};
this.connection.onLogs('all', this._onLog, 'confirmed');
for (const [address, info] of this.loadedAccountInfos) {
this.update(address, 0, info);
}
for (const [label, keypair] of this.loadedKeypairs) {
this.keypairs.set(keypair.publicKey.toBase58(), { keypair, id: label });
}
}
get paused() {
return this._paused;
}
set paused(val) {
this._paused = val;
}
// -----------------
// Account States
// -----------------
async update(address, slot, accountInfo) {
var _a;
if (!this.states.has(address)) {
this.states.set(address, new AccountStateTracker());
}
const res = await this.accountProvider.tryResolveAccount(new web3_js_1.PublicKey(address), accountInfo);
if (res == null)
return;
this.add(address, { ...res, slot });
const states = (_a = this.get(address)) === null || _a === void 0 ? void 0 : _a.relayStates;
this.emit(`account-changed:${address}`, states);
}
add(address, state) {
const states = this.get(address);
(0, assert_1.strict)(states != null, 'expected states to be set before adding');
states.add(state);
}
get(address) {
return this.states.get(address);
}
accountStateForSlot(address, slot) {
var _a;
return (_a = this.get(address)) === null || _a === void 0 ? void 0 : _a.accountStateForSlot(slot);
}
accountDataForSlot(address, slot) {
var _a;
return (_a = this.get(address)) === null || _a === void 0 ? void 0 : _a.accountDataForSlot(slot);
}
allAccountAddresses() {
return Array.from(this.states.keys());
}
// -----------------
// Keypairs
// -----------------
storeKeypair(id, keypair) {
this.keypairs.set(keypair.publicKey.toBase58(), { keypair, id });
}
labelKeypairs(
// Keyed pubkey:label
labels) {
for (const [key, label] of Object.entries(labels)) {
const entry = this.keypairs.get(key);
if (entry == null)
continue;
this.keypairs.set(key, { keypair: entry.keypair, id: label });
}
}
get allKeypairs() {
return this.keypairs;
}
getKeypairById(keypairId) {
for (const { keypair, id } of this.keypairs.values()) {
if (id === keypairId)
return keypair;
}
}
getKeypairByAddress(address) {
var _a;
return (_a = this.keypairs.get(address)) === null || _a === void 0 ? void 0 : _a.keypair;
}
static get instance() {
(0, assert_1.strict)(AccountStates._instance != null, 'expected AccountStates instance');
return AccountStates._instance;
}
static createInstance(connection, accountProvider, loadedAccountInfos, loadedKeypairs) {
AccountStates._instance = new AccountStates(connection, accountProvider, loadedAccountInfos, loadedKeypairs);
return AccountStates._instance;
}
}
exports.AccountStates = AccountStates;
AccountStates._instance = null;
function printableAccount(account) {
const prettified = {};
for (const [key, val] of Object.entries(account)) {
if (val == null)
continue;
if (typeof val.pretty === 'function') {
prettified[key] = val.pretty();
}
if (bn_js_1.default.isBN(val) ||
(typeof val === 'object' &&
'negative' in val &&
'words' in val &&
'red' in val)) {
prettified[key] = new bn_js_1.default(val).toNumber();
}
else if ((0, amman_client_1.isKeyLike)(val)) {
prettified[key] = (0, amman_client_1.publicKeyString)(val);
}
else if (Array.isArray(val)) {
prettified[key] = val.map((val) => JSON.stringify(printableAccount(val)));
}
else if (typeof val === 'object') {
prettified[key] = JSON.stringify(printableAccount(val), null, 2);
}
else {
prettified[key] = val;
}
}
return prettified;
}
exports.printableAccount = printableAccount;
//# sourceMappingURL=state.js.map