@57block/stellar-resource-usage
Version:
A library that provides convenient ways to monitor and analyze the resources consumed by smart contracts during execution
137 lines • 5.74 kB
JavaScript
import { transform, isObject, isArray } from 'lodash-es';
import { STELLAR_LIMITS_CONFIG, UPDATE_STELLAR_LIMITS_CONFIG } from '../constants';
import { getProtocolHistory } from '@/api/stellar';
// This time corresponds to the value in the STELLAR_LIMITS_CONFIG
let TIMESTAMP = 1735537317419;
export const updateTxLimits = async () => {
if (Date.now() - TIMESTAMP < 60 * 60 * 1000)
return;
TIMESTAMP = Date.now();
try {
const data = await getProtocolHistory();
const flattenData = data.map((history) => {
return filterObject(history.config_changes, ['contractCompute', 'contractLedgerCost', 'contractBandwidth']);
});
const contractLimitData = flattenData.reverse().reduce((final, item) => {
return { ...final, ...item };
}, {});
// TODO: didn't update label, because it may be deleted.
const txLimits = {
...STELLAR_LIMITS_CONFIG,
cpu_insns: {
...STELLAR_LIMITS_CONFIG.cpu_insns,
value: contractLimitData.contractCompute.txMaxInstructions ?? STELLAR_LIMITS_CONFIG.cpu_insns.value,
},
mem_bytes: {
...STELLAR_LIMITS_CONFIG.mem_bytes,
value: contractLimitData.contractLedgerCost.txMemoryLimit ?? STELLAR_LIMITS_CONFIG.mem_bytes.value,
},
entry_reads: {
...STELLAR_LIMITS_CONFIG.entry_reads,
value: contractLimitData.contractLedgerCost.txMaxReadLedgerEntries ?? STELLAR_LIMITS_CONFIG.entry_reads.value,
},
entry_writes: {
...STELLAR_LIMITS_CONFIG.entry_writes,
value: contractLimitData.contractLedgerCost.txMaxWriteLedgerEntries ?? STELLAR_LIMITS_CONFIG.entry_writes.value,
},
read_bytes: {
...STELLAR_LIMITS_CONFIG.read_bytes,
value: contractLimitData.contractLedgerCost.txMaxReadBytes ?? STELLAR_LIMITS_CONFIG.read_bytes.value,
},
write_bytes: {
...STELLAR_LIMITS_CONFIG.write_bytes,
value: contractLimitData.contractLedgerCost.txMaxWriteBytes ?? STELLAR_LIMITS_CONFIG.write_bytes.value,
},
min_txn_bytes: {
...STELLAR_LIMITS_CONFIG.min_txn_bytes,
value: contractLimitData.contractBandwidth.txMaxSizeBytes ?? STELLAR_LIMITS_CONFIG.min_txn_bytes.value,
},
};
UPDATE_STELLAR_LIMITS_CONFIG(txLimits);
}
catch (error) {
console.log(error);
}
};
export function filterObject(obj, keys) {
if (Object.prototype.toString.call(obj) === '[object Object]') {
const filteredObj = {};
Object.entries(obj).forEach(([key, value]) => {
if (keys.includes(key)) {
filteredObj[key] = value;
}
});
return filteredObj;
}
}
export const flattenObjectWithFilter = (obj, allowedPaths, prefix = '') => {
return transform(obj, (result, value, key) => {
const newKey = prefix ? `${prefix}.${key}` : key;
const shouldInclude = allowedPaths.some((allowedPath) => {
if (newKey === allowedPath)
return true;
if (newKey.startsWith(allowedPath + '.'))
return true;
if (allowedPath.startsWith(newKey + '.'))
return true;
return false;
});
if (!shouldInclude)
return;
if (isObject(value) && !isArray(value)) {
Object.assign(result, flattenObjectWithFilter(value, allowedPaths, newKey));
}
else {
result[newKey] = value;
}
}, {});
};
/**
* Keep calling a `fn` for `timeoutInSeconds` seconds, if `keepWaitingIf` is
* true. Returns an array of all attempts to call the function.
* @private
*/
export async function withExponentialBackoff(
/** Function to call repeatedly */
fn,
/** Condition to check when deciding whether or not to call `fn` again */
keepWaitingIf,
/** Maximum total duration in seconds for all retry attempts */
timeoutInSeconds,
/** What to multiply `timeoutInSeconds` by, each subsequent attempt */
exponentialFactor = 1.5,
/** Whether to log extra info */
verbose = false) {
const attempts = [];
let count = 0;
attempts.push(await fn());
if (!keepWaitingIf(attempts[attempts.length - 1]))
return attempts;
const waitUntil = new Date(Date.now() + timeoutInSeconds * 1000).valueOf();
let waitTime = 1000;
let totalWaitTime = waitTime;
while (Date.now() < waitUntil && keepWaitingIf(attempts[attempts.length - 1])) {
count += 1;
// Wait a beat
if (verbose) {
console.info(`Waiting ${waitTime}ms before trying again (bringing the total wait time to ${totalWaitTime}ms so far, of total ${timeoutInSeconds * 1000}ms)`);
}
await new Promise((res) => setTimeout(res, waitTime));
// Exponential backoff
waitTime *= exponentialFactor;
if (new Date(Date.now() + waitTime).valueOf() > waitUntil) {
waitTime = waitUntil - Date.now();
if (verbose) {
console.info(`was gonna wait too long; new waitTime: ${waitTime}ms`);
}
}
totalWaitTime = waitTime + totalWaitTime;
// Try again
attempts.push(await fn(attempts[attempts.length - 1]));
if (verbose && keepWaitingIf(attempts[attempts.length - 1])) {
console.info(`${count}. Called ${fn}; ${attempts.length} prev attempts. Most recent: ${JSON.stringify(attempts[attempts.length - 1], null, 2)}`);
}
}
return attempts;
}
//# sourceMappingURL=utils.js.map