Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add decodeEvents function #130

Merged
merged 7 commits into from
Jul 5, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -779,11 +779,7 @@
"value": "guardian signature invalid"
}
],
"builtins": [
FabijanC marked this conversation as resolved.
Show resolved Hide resolved
"pedersen",
"range_check",
"ecdsa"
],
"builtins": ["pedersen", "range_check", "ecdsa"],
"data": [
"0x40780017fff7fff",
"0x1",
Expand Down
2 changes: 1 addition & 1 deletion scripts/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ CONFIG_FILE_NAME="hardhat.config.ts"

# setup example repo
rm -rf starknet-hardhat-example
git clone -b plugin --single-branch git@github.com:Shard-Labs/starknet-hardhat-example.git
git clone -b decode-events-example --single-branch git@github.com:Shard-Labs/starknet-hardhat-example.git
cd starknet-hardhat-example
git log -n 1
npm install
Expand Down
61 changes: 61 additions & 0 deletions src/adapt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,67 @@ export function adaptOutputUtil(
}
return adapted;
}
/**
* Adapts the string resulting from a Starknet CLI function call.
* This is done according to the actual output type specifed by the called function.
*
* @param rawResult the actual result in the form of an unparsed string
* @param datas array of starknet types in the expected event data
* @param abi the ABI of the contract whose function was called
*/
export function adaptEventUtil(
FabijanC marked this conversation as resolved.
Show resolved Hide resolved
rawResult: string,
datas: starknet.Argument[],
abi: starknet.Abi
): StringMap {
const splitStr = rawResult.split(" ");
const result: bigint[] = [];
for (const num of splitStr) {
const parsed = num[0] === "-" ? BigInt(num.substring(1)) * BigInt(-1) : BigInt(num);
result.push(parsed);
}
let resultIndex = 0;
let lastSpec: starknet.Argument = { type: null, name: null };
const adapted: StringMap = {};

for (const data of datas) {
const currentValue = result[resultIndex];
if (data.type === "felt") {
adapted[data.name] = currentValue;
resultIndex++;
} else if (data.type.endsWith("*")) {
// Assuming lastSpec refers to the array size argument; not checking its name - done during compilation
if (lastSpec.type !== "felt") {
const msg = `Array size argument (felt) must appear right before ${data.name} (${data.type}).`;
throw new HardhatPluginError(PLUGIN_NAME, msg);
}

// Remove * from the spec type
const dataArrayElementType = data.type.slice(0, -1);
const arrLength = parseInt(adapted[lastSpec.name]);

const structArray = [];

// Iterate over the struct array, starting at index, starting at `resultIndex`
for (let i = 0; i < arrLength; i++) {
// Generate a struct with each element of the array and push it to `structArray`
const ret = generateComplexOutput(result, resultIndex, dataArrayElementType, abi);
structArray.push(ret.generatedComplex);
// Next index is the proper raw index returned from generating the struct, which accounts for nested structs
resultIndex = ret.newRawIndex;
}
// New resultIndex is the raw index generated from the last struct
adapted[data.name] = structArray;
} else {
const ret = generateComplexOutput(result, resultIndex, data.type, abi);
adapted[data.name] = ret.generatedComplex;
resultIndex = ret.newRawIndex;
}

lastSpec = data;
}
return adapted;
}

/**
* Uses the numbers in the `raw` array to generate a tuple/struct of the provided `type`.
Expand Down
13 changes: 6 additions & 7 deletions src/devnet-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export interface IncreaseTimeResponse {
}

export class DevnetUtils implements Devnet {
constructor(private hre: HardhatRuntimeEnvironment) { }
constructor(private hre: HardhatRuntimeEnvironment) {}

private get endpoint() {
return `${this.hre.config.starknet.networkUrl}`;
Expand Down Expand Up @@ -94,18 +94,17 @@ export class DevnetUtils implements Devnet {
`${this.endpoint}/increase_time`,
{
time: seconds
});
}
);
return response.data;
}, "Request failed. Make sure your network has the /increase_time endpoint");
}

public async setTime(seconds: number) {
return this.withErrorHandler<SetTimeResponse>(async () => {
const response = await axios.post<SetTimeResponse>(
`${this.endpoint}/set_time`,
{
time: seconds
});
const response = await axios.post<SetTimeResponse>(`${this.endpoint}/set_time`, {
time: seconds
});
return response.data;
}, "Request failed. Make sure your network has the /set_time endpoint");
}
Expand Down
2 changes: 1 addition & 1 deletion src/extend-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export function shortStringToBigIntUtil(convertableString: string) {
return BigInt("0x" + charArray.join(""));
}

export function bigIntToShortStringUtil(convertableBigInt: BigInt) {
FabijanC marked this conversation as resolved.
Show resolved Hide resolved
export function bigIntToShortStringUtil(convertableBigInt: bigint) {
return Buffer.from(convertableBigInt.toString(16), "hex").toString();
}

Expand Down
9 changes: 8 additions & 1 deletion src/starknet-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,14 @@ export interface CairoFunction {
outputs: Argument[];
}

export type AbiEntry = CairoFunction | Struct;
export interface CairoEvent {
data: Argument[];
FabijanC marked this conversation as resolved.
Show resolved Hide resolved
keys: Argument[];
name: string;
type: "event";
}

export type AbiEntry = CairoFunction | Struct | CairoEvent;

export interface Abi {
[name: string]: AbiEntry;
Expand Down
4 changes: 2 additions & 2 deletions src/type-extensions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,14 +167,14 @@ declare module "hardhat/types/runtime" {
* @param input the input short string
* @returns the numeric equivalent of the input short string, wrapped in a `BigInt`
*/
shortStringToBigInt: (convertableString: string) => BigInt;
FabijanC marked this conversation as resolved.
Show resolved Hide resolved
shortStringToBigInt: (convertableString: string) => bigint;

/**
* Converts a BigInt to a string. The opposite of {@link shortStringToBigInt}.
* @param input the input BigInt
* @returns a string which is the result of converting a BigInt's hex value to its ASCII equivalent
*/
bigIntToShortString: (convertableBigInt: BigInt) => string;
bigIntToShortString: (convertableBigInt: bigint) => string;

/**
* The selected starknet-network name.
Expand Down
50 changes: 48 additions & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import * as starknet from "./starknet-types";
import { HardhatPluginError } from "hardhat/plugins";
import { PLUGIN_NAME, CHECK_STATUS_TIMEOUT, CHECK_STATUS_RECOVER_TIMEOUT } from "./constants";
import { adaptLog, copyWithBigint } from "./utils";
import { adaptInputUtil, adaptOutputUtil } from "./adapt";
import { adaptInputUtil, adaptOutputUtil, adaptEventUtil } from "./adapt";
import { StarknetWrapper } from "./starknet-wrappers";
import { Wallet } from "hardhat/types";

import { hash } from "starknet";
FabijanC marked this conversation as resolved.
Show resolved Hide resolved
/**
* According to: https://starknet.io/docs/hello_starknet/intro.html#interact-with-the-contract
* Not using an enum to avoid code duplication and reverse mapping.
Expand Down Expand Up @@ -700,4 +700,50 @@ export class StarknetContract {
const func = <starknet.CairoFunction>this.abi[functionName];
return adaptOutputUtil(rawResult, func.outputs, this.abi);
}

/**
* Adapt `Event` to something more readable .
* @param eventNames an array of the event's name that was emitted
* @param rawResult array of the function output as as unparsed space separated string
* @returns structured output
*/
adaptEvent(eventNames: string[], rawResult: string[]) {
FabijanC marked this conversation as resolved.
Show resolved Hide resolved
const events: StringMap[] = [];
for (let i = 0; i < eventNames.length; i++) {
const func = <starknet.CairoEvent>this.abi[eventNames[i]];
FabijanC marked this conversation as resolved.
Show resolved Hide resolved
const adapted = adaptEventUtil(rawResult[i], func.data, this.abi);
events.push(adapted);
}
return events;
}

/**
* decode Events to an array with all the arguments emmitted by the events.
FabijanC marked this conversation as resolved.
Show resolved Hide resolved
* @param events array of unstructured events
FabijanC marked this conversation as resolved.
Show resolved Hide resolved
* @returns array of the arguments emmitted by the events
*/
async decodeEvents(events: starknet.Event[]): Promise<StringMap[]> {
FabijanC marked this conversation as resolved.
Show resolved Hide resolved
const rawResult: string[] = [];
FabijanC marked this conversation as resolved.
Show resolved Hide resolved
let rawResultIndex = 0;
for (const event of events) {
const result = event.data.map(BigInt).join(" ");
rawResult[rawResultIndex] = result;
rawResultIndex++;
}
const eventNames: string[] = [];
rawResultIndex = 0;
for (const functionName in this.abi) {
FabijanC marked this conversation as resolved.
Show resolved Hide resolved
const func = this.abi[functionName];
if (func.type == "event") {
FabijanC marked this conversation as resolved.
Show resolved Hide resolved
for (const event of events) {
const result = hash.getSelectorFromName(func.name);
FabijanC marked this conversation as resolved.
Show resolved Hide resolved
if (event.keys[0] == result) {
FabijanC marked this conversation as resolved.
Show resolved Hide resolved
eventNames[rawResultIndex] = func.name;
rawResultIndex++;
}
}
}
}
return this.adaptEvent(eventNames, rawResult);
}
}