@near-lake/primitives
Version:
Near Protocol primitive datatypes utilized by near-lake-framework and QueryAPI
231 lines (230 loc) • 11.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.BlockHeader = exports.Block = void 0;
const receipts_1 = require("./receipts");
const events_1 = require("./events");
const stateChanges_1 = require("./stateChanges");
const helpers_1 = require("../helpers");
const functionCallView_1 = require("./functionCallView");
/**
* The `Block` type is used to represent a block in the NEAR Lake Framework.
*
* **Important Notes on `Block`:**
* - All the entities located on different shards were merged into one single list without differentiation.
* - `Block` is not the fairest name for this structure either. NEAR Protocol is a sharded blockchain, so its block is actually an ephemeral structure that represents a collection of real blocks called chunks in NEAR Protocol.
*/
class Block {
constructor(
/**
* Low-level structure for backward compatibility.
* As implemented in previous versions of [`near-lake-framework`](https://www.npmjs.com/package/near-lake-framework).
*/
streamerMessage, executedReceipts,
/**
* Receipts included on the chain but not executed yet marked as “postponed”: they are represented by the same structure `Receipt` (see the corresponding section in this doc for more details).
*/
postponedReceipts,
/**
* List of included `Transactions`, converted into `Receipts`.
*
* **_NOTE_:** Heads up! You might want to know about `Transactions` to know where the action chain has begun. Unlike Ethereum, where a Transaction contains everything you may want to know about a particular interaction on the Ethereum blockchain, Near Protocol because of its asynchronous nature converts a `Transaction` into a `Receipt` before executing it. Thus, On NEAR, `Receipts` are more important for figuring out what happened on-chain as a result of a Transaction signed by a user. Read more about [Transactions on Near](https://nomicon.io/RuntimeSpec/Transactions) here.
*
*/
transactions, _actions, _events, _stateChanges) {
this.streamerMessage = streamerMessage;
this.executedReceipts = executedReceipts;
this.postponedReceipts = postponedReceipts;
this.transactions = transactions;
this._actions = _actions;
this._events = _events;
this._stateChanges = _stateChanges;
}
/**
* Returns the block hash. A shortcut to get the data from the block header.
*/
get blockHash() {
return this.header().hash;
}
/**
* Returns the previous block hash. A shortcut to get the data from the block header.
*/
get prevBlockHash() {
return this.header().prevHash;
}
/**
* Returns the block height. A shortcut to get the data from the block header.
*/
get blockHeight() {
return this.header().height;
}
/**
* Returns the block date in ISO format, e.g. 2022-01-01.
*/
get blockDate() {
return new Date(this.streamerMessage.block.header.timestamp / 1000000)
.toISOString()
.substring(0, 10);
}
/**
* Returns a `BlockHeader` structure of the block
* See `BlockHeader` structure sections for details.
*/
header() {
return BlockHeader.fromStreamerMessage(this.streamerMessage);
}
/**
* Returns a slice of `Receipts` executed in the block.
* Basically is a getter for the `executedReceipts` field.
*/
receipts() {
if (this.executedReceipts.length == 0) {
this.executedReceipts = this.streamerMessage.shards
.flatMap((shard) => shard.receiptExecutionOutcomes)
.map((executionReceipt) => receipts_1.Receipt.fromOutcomeWithReceipt(executionReceipt));
}
return this.executedReceipts;
}
/**
* Returns an Array of `Actions` executed in the block.
*/
actions() {
return this.streamerMessage.shards
.flatMap((shard) => shard.receiptExecutionOutcomes)
.filter((executionOutcomeWithReceipt) => receipts_1.Action.isActionReceipt(executionOutcomeWithReceipt.receipt))
.map((executionOutcomeWithReceipt) => receipts_1.Action.fromOutcomeWithReceipt(executionOutcomeWithReceipt))
.filter((action) => action !== null);
}
/**
* Returns an Array of function calls executed in the block matching provided filters.
* @param receiverFilter - filter by contract name (e.g. `*.pool.near,*.poolv1.near`). Default is `*` (all contracts).
* @param statusFilter - filter by receipt status (all|onlySuccessful|onlyFailed). Default is `onlySuccessful`.
*/
functionCalls(receiverFilter = "*", statusFilter = "onlySuccessful") {
return this.actions()
.filter((action) => (0, helpers_1.isMatchingReceiver)(action.receiverId, receiverFilter) &&
(0, helpers_1.isMatchingReceiptStatus)(action.receiptStatus, statusFilter))
.flatMap((a) => a.operations
.filter((op) => op.hasOwnProperty("FunctionCall"))
.map((op) => functionCallView_1.FunctionCallView.fromFunctionCall(Object(op).FunctionCall, a)));
}
/**
* Returns an Array of function calls to receivers matching receiverFilter.
* @param receiverFilter - filter by contract name (e.g. `*.pool.near,*.poolv1.near`). Default is `*` (all contracts).
* @param method - name of the method to filter by. Returns all function calls to receiverFilter if not provided.
* @param statusFilter - filter by receipt status (all|onlySuccessful|onlyFailed). Default is `onlySuccessful`.
*/
functionCallsToReceiver(receiverFilter = "*", method, statusFilter = "onlySuccessful") {
return this.functionCalls(receiverFilter, statusFilter).filter((call) => method ? call.methodName === method : true);
}
/**
* Returns `Events` emitted in the block.
*/
events() {
const events = this.receipts().flatMap((executedReceipt) => executedReceipt.logs
.filter(events_1.RawEvent.isEvent)
.map(events_1.RawEvent.fromLog)
.map((rawEvent) => {
return new events_1.Event(executedReceipt.receiptId, rawEvent);
}));
return events;
}
/**
* Returns raw logs regardless of the fact that they are standard events or not.
*/
logs() {
const logs = this.receipts().flatMap((executedReceipt) => executedReceipt.logs.map((rawLog) => {
let log = {
relatedReceiptId: executedReceipt.receiptId,
log: rawLog,
};
return log;
}));
return logs;
}
/**
* Returns an Array of `StateChange` occurred in the block.
*/
stateChanges() {
if (this._stateChanges.length == 0) {
this._stateChanges = this.streamerMessage.shards
.flatMap((shard) => shard.stateChanges)
.map(stateChanges_1.StateChange.fromStateChangeView);
}
return this._stateChanges;
}
/**
* Returns `Action` of the provided `receipt_id` from the block if any. Returns `undefined` if there is no corresponding `Action`.
*
* This method uses the internal `Block` `action` field which is empty by default and will be filled with the block’s actions on the first call to optimize memory usage.
*
* The result is either `Action | undefined` since there might be a request for an `Action` by `receipt_id` from another block, in which case this method will be unable to find the `Action` in the current block. In the other case, the request might be for an `Action` for a `receipt_id` that belongs to a `DataReceipt` where an action does not exist.
*/
actionByReceiptId(receipt_id) {
if (this._actions.size == 0) {
this.buildActionsHashmap();
}
return this._actions.get(receipt_id);
}
/**
* Returns an Array of Events emitted by `ExecutionOutcome` for the given `receipt_id`. There might be more than one `Event` for the `Receipt` or there might be none of them. In the latter case, this method returns an empty Array.
*/
eventsByReceiptId(receipt_id) {
if (this._events.size == 0) {
this.buildEventsHashmap();
}
return this._events.get(receipt_id) || [];
}
/**
* Returns an Array of Events emitted by `ExecutionOutcome` for the given `account_id`. There might be more than one `Event` for the `Receipt` or there might be none of them. In the latter case, this method returns an empty Array.
*/
eventsByAccountId(account_id) {
return this.events().filter((event) => {
const action = this.actionByReceiptId(event.receiptId);
return (action === null || action === void 0 ? void 0 : action.receiverId) == account_id || (action === null || action === void 0 ? void 0 : action.signerId) == account_id;
});
}
buildActionsHashmap() {
const actions = new Map();
this.actions().forEach((action) => {
actions.set(action.receiptId, action);
});
this._actions = actions;
}
buildEventsHashmap() {
const events = new Map();
for (const receipt of this.executedReceipts) {
events.set(receipt.receiptId, receipt.events);
}
return events;
}
static fromStreamerMessage(streamerMessage) {
return new Block(streamerMessage, [], [], [], new Map(), new Map(), []);
}
}
exports.Block = Block;
/**
* Replacement for `BlockHeaderView` from [near-primitives](https://github.com/near/nearcore/tree/master/core/primitives). Shrunken and simplified.
*
* **Note:** the original `BlockHeaderView` is still accessible via the `.streamerMessage` attribute.
*/
class BlockHeader {
constructor(height, hash, prevHash, author, timestampNanosec, epochId, nextEpochId, gasPrice, totalSupply, latestProtocolVersion, randomValue, chunksIncluded, validatorProposals) {
this.height = height;
this.hash = hash;
this.prevHash = prevHash;
this.author = author;
this.timestampNanosec = timestampNanosec;
this.epochId = epochId;
this.nextEpochId = nextEpochId;
this.gasPrice = gasPrice;
this.totalSupply = totalSupply;
this.latestProtocolVersion = latestProtocolVersion;
this.randomValue = randomValue;
this.chunksIncluded = chunksIncluded;
this.validatorProposals = validatorProposals;
}
static fromStreamerMessage(streamerMessage) {
return new BlockHeader(streamerMessage.block.header.height, streamerMessage.block.header.hash, streamerMessage.block.header.prevHash, streamerMessage.block.author, streamerMessage.block.header.timestampNanosec, streamerMessage.block.header.epochId, streamerMessage.block.header.nextEpochId, streamerMessage.block.header.gasPrice, streamerMessage.block.header.totalSupply, streamerMessage.block.header.latestProtocolVersion, streamerMessage.block.header.randomValue, streamerMessage.block.header.chunksIncluded, streamerMessage.block.header.validatorProposals);
}
}
exports.BlockHeader = BlockHeader;