Skip to content

Commit

Permalink
Block helper functions for successful function calls and social opera…
Browse files Browse the repository at this point in the history
…tions
  • Loading branch information
gabehamilton committed Apr 30, 2024
1 parent d33ca61 commit dc2a472
Show file tree
Hide file tree
Showing 10 changed files with 93,228 additions and 14 deletions.
5 changes: 5 additions & 0 deletions .changeset/quiet-singers-tan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@near-lake/framework": patch
---

Upgrade near-primitives to 0.5.0
5 changes: 5 additions & 0 deletions .changeset/ten-suits-peel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@near-lake/primitives": minor
---

Block helper methods for successfulFunctionCalls and socialOperations
93,062 changes: 93,062 additions & 0 deletions blocks/117969098.json

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions packages/near-lake-framework/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@near-lake/framework",
"version": "0.1.3",
"version": "0.1.4",
"description": "JS Library to connect to the NEAR Lake S3 and stream the data",
"author": "NEAR Inc <[email protected]>",
"keywords": [
Expand All @@ -27,6 +27,6 @@
},
"dependencies": {
"@aws-sdk/client-s3": "^3.32.0",
"@near-lake/primitives": "^0.4.0"
"@near-lake/primitives": "^0.5.0"
}
}
2 changes: 1 addition & 1 deletion packages/near-lake-primitives/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@near-lake/primitives",
"version": "0.4.0",
"version": "0.5.0",
"keywords": [
"lake-primitives",
"near-indexer"
Expand Down
103 changes: 98 additions & 5 deletions packages/near-lake-primitives/src/types/block.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import { Action, Receipt } from './receipts';
import { StreamerMessage, ValidatorStakeView } from './core/types';
import { Action, Receipt, Operation, FunctionCall, FunctionCallWrapper } from './receipts';
import { StreamerMessage, ValidatorStakeView } from "./core/types";
import { Transaction } from './transactions';
import { Event, RawEvent, Log } from './events';
import { StateChange } from './stateChanges';

export type DecodedFunctionCall = {
methodName: string;
args: any;
receiptId: string;
}

/**
* The `Block` type is used to represent a block in the NEAR Lake Framework.
*
Expand Down Expand Up @@ -79,11 +85,14 @@ export class Block {
/**
* Returns an Array of `Actions` executed in the block.
*/
actions(): Action[] {
actions(successfulOnly?: boolean): Action[] {
const actions: Action[] = this.streamerMessage.shards
.flatMap((shard) => shard.receiptExecutionOutcomes)
.filter((exeuctionOutcomeWithReceipt) => Action.isActionReceipt(exeuctionOutcomeWithReceipt.receipt))
.map((exeuctionOutcomeWithReceipt) => Action.fromReceiptView(exeuctionOutcomeWithReceipt.receipt))
.filter((executionOutcomeWithReceipt) =>
Action.isActionReceipt(executionOutcomeWithReceipt.receipt) &&
(!successfulOnly || (executionOutcomeWithReceipt.executionOutcome.outcome.status as {SuccessValue: any}))
)
.map((executionOutcomeWithReceipt) => Action.fromReceiptView(executionOutcomeWithReceipt.receipt))
.filter((action): action is Action => action !== null)
.map(action => action)
return actions
Expand Down Expand Up @@ -157,6 +166,90 @@ export class Block {
});
}

/**
* Decodes base64 encoded JSON data. Returns `undefined` if the data is not in the expected format.
* @param encodedValue
*/
base64decode(encodedValue: any) {
try {
const buff = Buffer.from(encodedValue, "base64");
const str = buff.toString("utf-8").replace(/\\xa0/g, " ");
return JSON.parse(str);
} catch (error) {
console.error(
'Error parsing base64 JSON - skipping data',
error
);
}
}

/**
* Returns an Array of `DecodedFunctionCall` for the given `contract` and `method` if provided.
* If the `method` is not provided, it returns all the `DecodedFunctionCall`s for the given `contract`.
* Arguments to the function call are decoded from base64 to JSON.
* @param contract
* @param method
*/
successfulFunctionCalls(contract: string, method?: string) : DecodedFunctionCall[] {
return this
.actions(true)
.filter((action) => action.receiverId === contract)
.flatMap((action: Action) =>
action.operations
.map((operation: Operation): FunctionCall => (operation as FunctionCallWrapper)?.FunctionCall)
.filter((functionCallOperation) => functionCallOperation && (!method || functionCallOperation?.methodName === method))
.map((functionCallOperation) => ({
...functionCallOperation,
args: this.base64decode(functionCallOperation.args),
receiptId: action.receiptId,
}))
);
};

/**
* Returns data that follows the social.near contract template for key value data.
* @param operation The top level key to search for in each user's account space
* @param contract Defaults to 'social.near', pass in a different contract if needed
* @param method Defaults to 'set', pass in a different method if needed
*/
socialOperations(operation: string, contract: string = "social.near", method: string = "set") {
return this.successfulFunctionCalls(contract, method)
.filter((functionCall) => {
if (
!functionCall ||
!functionCall.args ||
!functionCall.args.data ||
!Object.keys(functionCall.args.data) ||
!Object.keys(functionCall.args.data)[0]
) {
console.error(
"Set operation did not have arg data in expected format"
);
return;
}
const accountId = Object.keys(functionCall.args.data)[0];
if (!functionCall.args.data[accountId]) {
console.error("Set operation did not have arg data for accountId");
return;
}
const accountData = functionCall.args.data[accountId];
if (!accountData) {
console.error(
"Set operation did not have arg data for accountId in expected format"
);
return;
}
return accountData[operation];
})
.map((functionCall) => {
const accountId = Object.keys(functionCall.args.data)[0];
return {
accountId,
data: functionCall.args.data[accountId][operation],
};
});
};

private buildActionsHashmap() {
const actions = new Map<string, Action>();
this.actions().forEach(action => {
Expand Down
6 changes: 4 additions & 2 deletions packages/near-lake-primitives/src/types/receipts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,9 @@ class DeployContract {
constructor(readonly code: Uint8Array) { }
};

class FunctionCall {
export type FunctionCallWrapper = {FunctionCall: FunctionCall};

export class FunctionCall {
constructor(readonly methodName: string, readonly args: Uint8Array, readonly gas: number, readonly deposit: string) { }
};

Expand Down Expand Up @@ -167,7 +169,7 @@ class DeleteAccount {
export type Operation =
| 'CreateAccount'
| DeployContract
| FunctionCall
| FunctionCallWrapper
| Transfer
| Stake
| AddKey
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,33 @@ exports[`Block parses event logs 1`] = `
]
`;

exports[`Block parses social operations 1`] = `
[
{
"accountId": "flatirons.near",
"data": "{"near":{"modelProvider":{"openai":{"displayName":"OpenAI","logoUrl":{"cid":"bafkreicc3vj3dmcyedrlsb57uq72ojnppsoh2sp3ozcxko347ytvd4amxi","name":"OpenAI_Logo.png","type":"image/png","size":8735},"tags":null,"documentationUrl":""}}}}",
},
]
`;

exports[`Block parses successful function calls 1`] = `
[
{
"args": {
"data": {
"flatirons.near": {
"entities": "{"near":{"modelProvider":{"openai":{"displayName":"OpenAI","logoUrl":{"cid":"bafkreicc3vj3dmcyedrlsb57uq72ojnppsoh2sp3ozcxko347ytvd4amxi","name":"OpenAI_Logo.png","type":"image/png","size":8735},"tags":null,"documentationUrl":""}}}}",
},
},
},
"deposit": "0",
"gas": 100000000000000,
"methodName": "set",
"receiptId": "DoY9niR8tgMxHve1a7dCq36Eo3uRMJZtvMRsPozr1mYS",
},
]
`;

exports[`Block serializes meta transactions 1`] = `
{
"Delegate": {
Expand Down
20 changes: 20 additions & 0 deletions packages/near-lake-primitives/test/block.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,24 @@ describe("Block", () => {
);
expect(authorToPostId).toMatchSnapshot();
});

it("parses successful function calls", async () => {
let streamerMessageBuffer = await readFile(
`${__dirname}/../../../blocks/117969098.json`
);
let streamerMessage = JSON.parse(streamerMessageBuffer.toString());
let block = Block.fromStreamerMessage(streamerMessage);

expect(block.successfulFunctionCalls("social.near")).toMatchSnapshot();
});

it("parses social operations", async () => {
let streamerMessageBuffer = await readFile(
`${__dirname}/../../../blocks/117969098.json`
);
let streamerMessage = JSON.parse(streamerMessageBuffer.toString());
let block = Block.fromStreamerMessage(streamerMessage);

expect(block.socialOperations("entities")).toMatchSnapshot();
});
});

0 comments on commit dc2a472

Please sign in to comment.