Skip to content

Add typecasting for tuple[] and address[] #1892

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

Merged
merged 6 commits into from
Jan 8, 2025
Merged
Show file tree
Hide file tree
Changes from all 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
5 changes: 5 additions & 0 deletions .changeset/wild-pears-yell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@graphprotocol/graph-cli': minor
---

handle tuple[] and address[] for event parameters - #949
2 changes: 1 addition & 1 deletion packages/cli/src/codegen/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export const unrollTuple = ({
path: string[];
index: number; // TODO: index is unused, do we really need it?
value: any;
}) =>
}): { path: string[]; type: string }[] =>
value.components.reduce((acc: any[], component: any, index: number) => {
const name = component.name || `value${index}`;
return acc.concat(
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/protocols/ethereum/scaffold/mapping.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export const generatePlaceholderHandlers = ({
entity.count = entity.count + BigInt.fromI32(1)

// Entity fields can be set based on event parameters
${generateEventFieldAssignments(event, contractName).slice(0, 2).join('\n')}
${generateEventFieldAssignments(event, contractName).assignments.slice(0, 2).join('\n')}

// Entities can be written to the store with \`.save()\`
entity.save()
Expand Down
171 changes: 162 additions & 9 deletions packages/cli/src/scaffold/__snapshots__/ethereum.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ dataSources:
entities:
- ExampleEvent
- ExampleEvent1
- TupleArrayEvent
abis:
- name: Contract
file: ./abis/Contract.json
Expand All @@ -29,6 +30,8 @@ dataSources:
handler: handleExampleEvent
- event: ExampleEvent(bytes32)
handler: handleExampleEvent1
- event: TupleArrayEvent((uint256,address)[],address[])
handler: handleTupleArrayEvent
file: ./src/contract.ts
"
`;
Expand All @@ -38,7 +41,8 @@ exports[`Ethereum subgraph scaffolding > Mapping (default) 1`] = `
import {
Contract,
ExampleEvent,
ExampleEvent1
ExampleEvent1,
TupleArrayEvent
} from "../generated/Contract/Contract"
import { ExampleEntity } from "../generated/schema"

Expand Down Expand Up @@ -89,15 +93,23 @@ export function handleExampleEvent(event: ExampleEvent): void {
}

export function handleExampleEvent1(event: ExampleEvent1): void {}

export function handleTupleArrayEvent(event: TupleArrayEvent): void {}
"
`;

exports[`Ethereum subgraph scaffolding > Mapping (for indexing events) 1`] = `
"import {
ExampleEvent as ExampleEventEvent,
ExampleEvent1 as ExampleEvent1Event
ExampleEvent1 as ExampleEvent1Event,
TupleArrayEvent as TupleArrayEventEvent
} from "../generated/Contract/Contract"
import { ExampleEvent, ExampleEvent1 } from "../generated/schema"
import {
ExampleEvent,
ExampleEvent1,
TupleArrayEvent
} from "../generated/schema"
import { Bytes } from "@graphprotocol/graph-ts"

export function handleExampleEvent(event: ExampleEventEvent): void {
let entity = new ExampleEvent(
Expand Down Expand Up @@ -134,6 +146,82 @@ export function handleExampleEvent1(event: ExampleEvent1Event): void {

entity.save()
}

export function handleTupleArrayEvent(event: TupleArrayEventEvent): void {
let entity = new TupleArrayEvent(
event.transaction.hash.concatI32(event.logIndex.toI32())
)
entity.tupleArray = changetype<Bytes[]>(event.params.tupleArray)
entity.addressArray = changetype<Bytes[]>(event.params.addressArray)

entity.blockNumber = event.block.number
entity.blockTimestamp = event.block.timestamp
entity.transactionHash = event.transaction.hash

entity.save()
}
"
`;

exports[`Ethereum subgraph scaffolding > Mapping handles tuple array type conversion 1`] = `
"import { BigInt, Bytes } from "@graphprotocol/graph-ts"
import {
Contract,
ExampleEvent,
ExampleEvent1,
TupleArrayEvent
} from "../generated/Contract/Contract"
import { ExampleEntity } from "../generated/schema"

export function handleExampleEvent(event: ExampleEvent): void {
// Entities can be loaded from the store using an ID; this ID
// needs to be unique across all entities of the same type
const id = event.transaction.hash.concat(
Bytes.fromByteArray(Bytes.fromBigInt(event.logIndex))
)
let entity = ExampleEntity.load(id)

// Entities only exist after they have been saved to the store;
// \`null\` checks allow to create entities on demand
if (!entity) {
entity = new ExampleEntity(id)

// Entity fields can be set using simple assignments
entity.count = BigInt.fromI32(0)
}

// BigInt and BigDecimal math are supported
entity.count = entity.count + BigInt.fromI32(1)

// Entity fields can be set based on event parameters
entity.a = event.params.a
entity.b = event.params.b

// Entities can be written to the store with \`.save()\`
entity.save()

// Note: If a handler doesn't require existing field values, it is faster
// _not_ to load the entity from the store. Instead, create it fresh with
// \`new Entity(...)\`, set the fields that should be updated and save the
// entity back to the store. Fields that were not set or unset remain
// unchanged, allowing for partial updates to be applied.

// It is also possible to access smart contracts from mappings. For
// example, the contract that has emitted the event can be connected to
// with:
//
// let contract = Contract.bind(event.address)
//
// The following functions can then be called on this contract to access
// state variables and other data:
//
// - contract.someVariable(...)
// - contract.getSomeValue(...)
}

export function handleExampleEvent1(event: ExampleEvent1): void {}

export function handleTupleArrayEvent(event: TupleArrayEvent): void {}
"
`;

Expand Down Expand Up @@ -173,6 +261,15 @@ type ExampleEvent1 @entity(immutable: true) {
blockTimestamp: BigInt!
transactionHash: Bytes!
}

type TupleArrayEvent @entity(immutable: true) {
id: Bytes!
tupleArray: [Bytes!]! # tuple[]
addressArray: [Bytes!]! # address[]
blockNumber: BigInt!
blockTimestamp: BigInt!
transactionHash: Bytes!
}
"
`;

Expand All @@ -185,7 +282,7 @@ exports[`Ethereum subgraph scaffolding > Test Files (default) 1`] = `
beforeAll,
afterAll
} from "matchstick-as/assembly/index"
import { BigInt, Bytes } from "@graphprotocol/graph-ts"
import { BigInt, Bytes, Address } from "@graphprotocol/graph-ts"
import { ExampleEvent } from "../generated/schema"
import { ExampleEvent as ExampleEventEvent } from "../generated/Contract/Contract"
import { handleExampleEvent } from "../src/contract"
Expand Down Expand Up @@ -257,8 +354,12 @@ describe("Describe entity assertions", () => {

exports[`Ethereum subgraph scaffolding > Test Files (default) 2`] = `
"import { newMockEvent } from "matchstick-as"
import { ethereum, BigInt, Bytes } from "@graphprotocol/graph-ts"
import { ExampleEvent, ExampleEvent1 } from "../generated/Contract/Contract"
import { ethereum, BigInt, Bytes, Address } from "@graphprotocol/graph-ts"
import {
ExampleEvent,
ExampleEvent1,
TupleArrayEvent
} from "../generated/Contract/Contract"

export function createExampleEventEvent(
a: BigInt,
Expand Down Expand Up @@ -305,6 +406,30 @@ export function createExampleEvent1Event(a: Bytes): ExampleEvent1 {

return exampleEvent1Event
}

export function createTupleArrayEventEvent(
tupleArray: Array<ethereum.Tuple>,
addressArray: Array<Address>
): TupleArrayEvent {
let tupleArrayEventEvent = changetype<TupleArrayEvent>(newMockEvent())

tupleArrayEventEvent.parameters = new Array()

tupleArrayEventEvent.parameters.push(
new ethereum.EventParam(
"tupleArray",
ethereum.Value.fromTupleArray(tupleArray)
)
)
tupleArrayEventEvent.parameters.push(
new ethereum.EventParam(
"addressArray",
ethereum.Value.fromAddressArray(addressArray)
)
)

return tupleArrayEventEvent
}
"
`;

Expand All @@ -317,7 +442,7 @@ exports[`Ethereum subgraph scaffolding > Test Files (for indexing events) 1`] =
beforeAll,
afterAll
} from "matchstick-as/assembly/index"
import { BigInt, Bytes } from "@graphprotocol/graph-ts"
import { BigInt, Bytes, Address } from "@graphprotocol/graph-ts"
import { ExampleEvent } from "../generated/schema"
import { ExampleEvent as ExampleEventEvent } from "../generated/Contract/Contract"
import { handleExampleEvent } from "../src/contract"
Expand Down Expand Up @@ -389,8 +514,12 @@ describe("Describe entity assertions", () => {

exports[`Ethereum subgraph scaffolding > Test Files (for indexing events) 2`] = `
"import { newMockEvent } from "matchstick-as"
import { ethereum, BigInt, Bytes } from "@graphprotocol/graph-ts"
import { ExampleEvent, ExampleEvent1 } from "../generated/Contract/Contract"
import { ethereum, BigInt, Bytes, Address } from "@graphprotocol/graph-ts"
import {
ExampleEvent,
ExampleEvent1,
TupleArrayEvent
} from "../generated/Contract/Contract"

export function createExampleEventEvent(
a: BigInt,
Expand Down Expand Up @@ -437,5 +566,29 @@ export function createExampleEvent1Event(a: Bytes): ExampleEvent1 {

return exampleEvent1Event
}

export function createTupleArrayEventEvent(
tupleArray: Array<ethereum.Tuple>,
addressArray: Array<Address>
): TupleArrayEvent {
let tupleArrayEventEvent = changetype<TupleArrayEvent>(newMockEvent())

tupleArrayEventEvent.parameters = new Array()

tupleArrayEventEvent.parameters.push(
new ethereum.EventParam(
"tupleArray",
ethereum.Value.fromTupleArray(tupleArray)
)
)
tupleArrayEventEvent.parameters.push(
new ethereum.EventParam(
"addressArray",
ethereum.Value.fromAddressArray(addressArray)
)
)

return tupleArrayEventEvent
}
"
`;
28 changes: 27 additions & 1 deletion packages/cli/src/scaffold/ethereum.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,32 @@ const TEST_CALLABLE_FUNCTIONS = [
},
];

const TEST_TUPLE_ARRAY_EVENT = {
name: 'TupleArrayEvent',
type: 'event',
inputs: [
{
name: 'tupleArray',
type: 'tuple[]',
components: [
{ name: 'field1', type: 'uint256' },
{ name: 'field2', type: 'address' },
],
},
{ name: 'addressArray', type: 'address[]' },
],
};

const TEST_ABI = new ABI(
'Contract',
undefined,
immutable.fromJS([TEST_EVENT, OVERLOADED_EVENT, TEST_CONTRACT, ...TEST_CALLABLE_FUNCTIONS]),
immutable.fromJS([
TEST_EVENT,
OVERLOADED_EVENT,
TEST_TUPLE_ARRAY_EVENT,
TEST_CONTRACT,
...TEST_CALLABLE_FUNCTIONS,
]),
);

const protocol = new Protocol('ethereum');
Expand Down Expand Up @@ -101,6 +123,10 @@ describe('Ethereum subgraph scaffolding', () => {
expect(await scaffoldWithIndexEvents.generateMapping()).toMatchSnapshot();
});

test('Mapping handles tuple array type conversion', async () => {
expect(await scaffold.generateMapping()).toMatchSnapshot();
});

test('Test Files (default)', async () => {
const files = await scaffoldWithIndexEvents.generateTests();
const testFile = files?.['contract.test.ts'];
Expand Down
Loading
Loading