@j0nnyboi/amman
Version:
A modern mandatory toolbelt to help test solana SDK libraries and apps on a locally running validator.
237 lines • 9.56 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AccountProvider = exports.DEFAULT_MINT_DECIMALS = void 0;
const amman_client_1 = require("@j0nnyboi/amman-client");
const safe_token_1 = require("@safecoin/safe-token");
const web3_js_1 = require("@safecoin/web3.js");
const bn_js_1 = __importDefault(require("bn.js"));
const numeral_1 = __importDefault(require("numeral"));
const utils_1 = require("../utils");
const types_1 = require("./types");
const AMMAN_TRACE_UNRESOLVED_ACCOUNTS = process.env.AMMAN_TRACE_UNRESOLVED_ACCOUNTS != null;
exports.DEFAULT_MINT_DECIMALS = 9;
function hasKnownByteSize(x) {
return typeof x.byteSize === 'number';
}
function isAmmanAccountProvider(x) {
const provider = x;
return (typeof provider.fromAccountInfo === 'function' &&
(hasKnownByteSize(provider) || typeof provider.byteSize === 'function'));
}
/**
* @private
*/
class AccountProvider {
constructor(providers, renderers) {
this.renderers = renderers;
/**
* providers by size
* size: 0 is used for providers of accounts that don't have a fixed size
*/
this.byByteSize = new Map();
this.nonfixedProviders = [];
this.connection = new web3_js_1.Connection(amman_client_1.LOCALHOST, 'confirmed');
this._mapProviders(providers);
}
static fromRecord(record, renderers) {
const providers = Object.values(record).filter(isAmmanAccountProvider);
return new AccountProvider(providers, renderers);
}
_mapProviders(providers) {
for (const provider of providers) {
const size = hasKnownByteSize(provider) ? provider.byteSize : 0;
if (size === 0) {
this.nonfixedProviders.push(provider);
}
else {
const providersForSize = this.byByteSize.get(size);
if (providersForSize == null) {
this.byByteSize.set(size, [provider]);
}
else {
providersForSize.push(provider);
}
}
}
const providersWithRender = providers.filter((x) => this.renderers.has(x));
(0, utils_1.logDebug)('Registered %d providers, %d of which have a renderer', providers.length, providersWithRender.length);
(0, utils_1.logTrace)({ providersBySize: this.byByteSize });
(0, utils_1.logTrace)({ providersUnknownSize: this.nonfixedProviders });
}
async tryResolveAccount(publicKey, accountInfo) {
var _a;
accountInfo !== null && accountInfo !== void 0 ? accountInfo : (accountInfo = (_a = (await this.connection.getAccountInfo(publicKey))) !== null && _a !== void 0 ? _a : undefined);
return accountInfo != null
? this._getProviderAndResolveAccount(accountInfo, publicKey)
: undefined;
}
async syncAccountInformation(publicKey) {
(0, utils_1.logTrace)(`Resolving account ${publicKey.toBase58()}`);
let accountInfo;
try {
accountInfo = await this.connection.getAccountInfo(publicKey, 'confirmed');
}
catch (err) {
(0, utils_1.logError)(err);
return;
}
if (accountInfo == null) {
(0, utils_1.logTrace)('Unable to find account info for', publicKey.toBase58());
return;
}
return this._getProviderAndResolveAccount(accountInfo, publicKey);
}
async _getProviderAndResolveAccount(accountInfo, publicKey) {
var _a;
if (accountInfo.lamports === 0 ||
accountInfo.executable ||
accountInfo.data.byteLength === 0) {
return;
}
let res = this._resolveFromProviderMatching(accountInfo, publicKey);
if (res != null) {
(0, utils_1.logTrace)(res);
return { ...res, data: accountInfo.data };
}
// No matching provider found, let's try the ones for non-fixed accounts or builtins from the token program
res =
(_a = this._tryResolveAccountFromProviders(this.nonfixedProviders, accountInfo)) !== null && _a !== void 0 ? _a : (await this._tryResolveAccountFromBuiltins(publicKey));
return {
account: res === null || res === void 0 ? void 0 : res.account,
rendered: res === null || res === void 0 ? void 0 : res.rendered,
data: accountInfo.data,
};
}
_resolveFromProviderMatching(accountInfo, publicKey) {
const providers = this.byByteSize.get(accountInfo.data.byteLength);
if (providers == null) {
(0, utils_1.logTrace)('Unable to find a provider by byteSize for %s', publicKey.toBase58());
(0, utils_1.logTrace)({
size: accountInfo.data.byteLength,
allProvidersByByteSize: this.byByteSize,
});
return;
}
(0, utils_1.logTrace)('Found providers for %s, %O', publicKey.toBase58(), providers);
return this._tryResolveAccountFromProviders(providers, accountInfo);
}
_tryResolveAccountFromProviders(providers, accountInfo) {
for (const provider of providers) {
try {
return this._resolveAccount(provider, accountInfo);
}
catch (err) {
if (AMMAN_TRACE_UNRESOLVED_ACCOUNTS) {
(0, utils_1.logTrace)(err);
}
}
}
}
async _tryResolveAccountFromBuiltins(address) {
for (const provider of [safe_token_1.getMint, safe_token_1.getAccount]) {
try {
const account = await provider(this.connection, address, 'singleGossip');
if (account != null) {
const ammanAccount = await this._toAmmanAccount(account);
return {
account: ammanAccount,
rendered: undefined,
};
}
}
catch (err) {
(0, utils_1.logTrace)(err);
}
}
}
_resolveAccount(provider, accountInfo) {
const [account] = provider.fromAccountInfo(accountInfo);
const render = this.renderers.get(provider);
const rendered = render != null ? render(account) : undefined;
return { account, rendered };
}
// -----------------
// Helpers
// -----------------
async _toAmmanAccount(account) {
const acc = {};
const amountDivisor = (0, types_1.isAccount)(account)
? (await this._getMintDecimals(account.mint)).divisor
: (0, types_1.isMint)(account)
? Math.pow(10, account.decimals)
: 1;
for (let [key, value] of Object.entries(account)) {
if (value == null) {
acc[key] = value;
}
else if ((0, amman_client_1.isKeyLike)(value)) {
const publicKeyStr = (0, amman_client_1.publicKeyString)(value);
const label = await this._tryResolveAddressRemote(publicKeyStr);
acc[key] = label == null ? publicKeyStr : `${label} (${publicKeyStr})`;
}
else if (typeof value === 'bigint') {
const formatted = (0, numeral_1.default)(value).format('0,0');
// Mint specific adjustments
if (key === 'amount' || key === 'supply') {
const balance = value / BigInt(amountDivisor);
acc[key] = formatted + ` (balance: ${balance.toString()})`;
}
else {
acc[key] = formatted;
}
}
else if (typeof value === 'number') {
acc[key] = (0, numeral_1.default)(value).format('0,0');
}
else if (bn_js_1.default.isBN(value) ||
(typeof value === 'object' &&
'negative' in value &&
'words' in value &&
'red' in value)) {
acc[key] = new bn_js_1.default(value).toNumber();
}
else if (typeof value.pretty === 'function') {
acc[key] = value.pretty();
}
else if (typeof value === 'object') {
acc[key] = JSON.stringify(value);
}
else {
acc[key] = value;
}
}
return {
pretty() {
return acc;
},
};
}
async _getMintDecimals(publicKey) {
let decimals;
try {
const mint = await (0, safe_token_1.getMint)(this.connection, publicKey, 'singleGossip');
decimals = mint.decimals;
}
catch (err) {
decimals = exports.DEFAULT_MINT_DECIMALS;
}
const divisor = Math.pow(10, decimals);
return { decimals, divisor };
}
async _tryResolveAddressRemote(publicKeyStr) {
try {
const instance = amman_client_1.Amman.existingInstance;
if (instance == null)
return;
return await instance.addr.resolveRemoteAddress(publicKeyStr);
}
catch (err) {
(0, utils_1.logError)(err);
}
}
}
exports.AccountProvider = AccountProvider;
//# sourceMappingURL=providers.js.map