Skip to content

Commit b4564a7

Browse files
Add typecasting for tuple[] and address[] (#1892)
* wrap address[] and tuple[] into changetype * tests * pass imports array * cleanup mapping codegen * changeset * add issue to changeset
1 parent d1fa5e0 commit b4564a7

File tree

6 files changed

+289
-43
lines changed

6 files changed

+289
-43
lines changed

.changeset/wild-pears-yell.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@graphprotocol/graph-cli': minor
3+
---
4+
5+
handle tuple[] and address[] for event parameters - #949

packages/cli/src/codegen/util.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export const unrollTuple = ({
4343
path: string[];
4444
index: number; // TODO: index is unused, do we really need it?
4545
value: any;
46-
}) =>
46+
}): { path: string[]; type: string }[] =>
4747
value.components.reduce((acc: any[], component: any, index: number) => {
4848
const name = component.name || `value${index}`;
4949
return acc.concat(

packages/cli/src/protocols/ethereum/scaffold/mapping.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export const generatePlaceholderHandlers = ({
3939
entity.count = entity.count + BigInt.fromI32(1)
4040
4141
// Entity fields can be set based on event parameters
42-
${generateEventFieldAssignments(event, contractName).slice(0, 2).join('\n')}
42+
${generateEventFieldAssignments(event, contractName).assignments.slice(0, 2).join('\n')}
4343
4444
// Entities can be written to the store with \`.save()\`
4545
entity.save()

packages/cli/src/scaffold/__snapshots__/ethereum.test.ts.snap

Lines changed: 162 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ dataSources:
2121
entities:
2222
- ExampleEvent
2323
- ExampleEvent1
24+
- TupleArrayEvent
2425
abis:
2526
- name: Contract
2627
file: ./abis/Contract.json
@@ -29,6 +30,8 @@ dataSources:
2930
handler: handleExampleEvent
3031
- event: ExampleEvent(bytes32)
3132
handler: handleExampleEvent1
33+
- event: TupleArrayEvent((uint256,address)[],address[])
34+
handler: handleTupleArrayEvent
3235
file: ./src/contract.ts
3336
"
3437
`;
@@ -38,7 +41,8 @@ exports[`Ethereum subgraph scaffolding > Mapping (default) 1`] = `
3841
import {
3942
Contract,
4043
ExampleEvent,
41-
ExampleEvent1
44+
ExampleEvent1,
45+
TupleArrayEvent
4246
} from "../generated/Contract/Contract"
4347
import { ExampleEntity } from "../generated/schema"
4448
@@ -89,15 +93,23 @@ export function handleExampleEvent(event: ExampleEvent): void {
8993
}
9094
9195
export function handleExampleEvent1(event: ExampleEvent1): void {}
96+
97+
export function handleTupleArrayEvent(event: TupleArrayEvent): void {}
9298
"
9399
`;
94100

95101
exports[`Ethereum subgraph scaffolding > Mapping (for indexing events) 1`] = `
96102
"import {
97103
ExampleEvent as ExampleEventEvent,
98-
ExampleEvent1 as ExampleEvent1Event
104+
ExampleEvent1 as ExampleEvent1Event,
105+
TupleArrayEvent as TupleArrayEventEvent
99106
} from "../generated/Contract/Contract"
100-
import { ExampleEvent, ExampleEvent1 } from "../generated/schema"
107+
import {
108+
ExampleEvent,
109+
ExampleEvent1,
110+
TupleArrayEvent
111+
} from "../generated/schema"
112+
import { Bytes } from "@graphprotocol/graph-ts"
101113
102114
export function handleExampleEvent(event: ExampleEventEvent): void {
103115
let entity = new ExampleEvent(
@@ -134,6 +146,82 @@ export function handleExampleEvent1(event: ExampleEvent1Event): void {
134146
135147
entity.save()
136148
}
149+
150+
export function handleTupleArrayEvent(event: TupleArrayEventEvent): void {
151+
let entity = new TupleArrayEvent(
152+
event.transaction.hash.concatI32(event.logIndex.toI32())
153+
)
154+
entity.tupleArray = changetype<Bytes[]>(event.params.tupleArray)
155+
entity.addressArray = changetype<Bytes[]>(event.params.addressArray)
156+
157+
entity.blockNumber = event.block.number
158+
entity.blockTimestamp = event.block.timestamp
159+
entity.transactionHash = event.transaction.hash
160+
161+
entity.save()
162+
}
163+
"
164+
`;
165+
166+
exports[`Ethereum subgraph scaffolding > Mapping handles tuple array type conversion 1`] = `
167+
"import { BigInt, Bytes } from "@graphprotocol/graph-ts"
168+
import {
169+
Contract,
170+
ExampleEvent,
171+
ExampleEvent1,
172+
TupleArrayEvent
173+
} from "../generated/Contract/Contract"
174+
import { ExampleEntity } from "../generated/schema"
175+
176+
export function handleExampleEvent(event: ExampleEvent): void {
177+
// Entities can be loaded from the store using an ID; this ID
178+
// needs to be unique across all entities of the same type
179+
const id = event.transaction.hash.concat(
180+
Bytes.fromByteArray(Bytes.fromBigInt(event.logIndex))
181+
)
182+
let entity = ExampleEntity.load(id)
183+
184+
// Entities only exist after they have been saved to the store;
185+
// \`null\` checks allow to create entities on demand
186+
if (!entity) {
187+
entity = new ExampleEntity(id)
188+
189+
// Entity fields can be set using simple assignments
190+
entity.count = BigInt.fromI32(0)
191+
}
192+
193+
// BigInt and BigDecimal math are supported
194+
entity.count = entity.count + BigInt.fromI32(1)
195+
196+
// Entity fields can be set based on event parameters
197+
entity.a = event.params.a
198+
entity.b = event.params.b
199+
200+
// Entities can be written to the store with \`.save()\`
201+
entity.save()
202+
203+
// Note: If a handler doesn't require existing field values, it is faster
204+
// _not_ to load the entity from the store. Instead, create it fresh with
205+
// \`new Entity(...)\`, set the fields that should be updated and save the
206+
// entity back to the store. Fields that were not set or unset remain
207+
// unchanged, allowing for partial updates to be applied.
208+
209+
// It is also possible to access smart contracts from mappings. For
210+
// example, the contract that has emitted the event can be connected to
211+
// with:
212+
//
213+
// let contract = Contract.bind(event.address)
214+
//
215+
// The following functions can then be called on this contract to access
216+
// state variables and other data:
217+
//
218+
// - contract.someVariable(...)
219+
// - contract.getSomeValue(...)
220+
}
221+
222+
export function handleExampleEvent1(event: ExampleEvent1): void {}
223+
224+
export function handleTupleArrayEvent(event: TupleArrayEvent): void {}
137225
"
138226
`;
139227

@@ -173,6 +261,15 @@ type ExampleEvent1 @entity(immutable: true) {
173261
blockTimestamp: BigInt!
174262
transactionHash: Bytes!
175263
}
264+
265+
type TupleArrayEvent @entity(immutable: true) {
266+
id: Bytes!
267+
tupleArray: [Bytes!]! # tuple[]
268+
addressArray: [Bytes!]! # address[]
269+
blockNumber: BigInt!
270+
blockTimestamp: BigInt!
271+
transactionHash: Bytes!
272+
}
176273
"
177274
`;
178275

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

258355
exports[`Ethereum subgraph scaffolding > Test Files (default) 2`] = `
259356
"import { newMockEvent } from "matchstick-as"
260-
import { ethereum, BigInt, Bytes } from "@graphprotocol/graph-ts"
261-
import { ExampleEvent, ExampleEvent1 } from "../generated/Contract/Contract"
357+
import { ethereum, BigInt, Bytes, Address } from "@graphprotocol/graph-ts"
358+
import {
359+
ExampleEvent,
360+
ExampleEvent1,
361+
TupleArrayEvent
362+
} from "../generated/Contract/Contract"
262363
263364
export function createExampleEventEvent(
264365
a: BigInt,
@@ -305,6 +406,30 @@ export function createExampleEvent1Event(a: Bytes): ExampleEvent1 {
305406
306407
return exampleEvent1Event
307408
}
409+
410+
export function createTupleArrayEventEvent(
411+
tupleArray: Array<ethereum.Tuple>,
412+
addressArray: Array<Address>
413+
): TupleArrayEvent {
414+
let tupleArrayEventEvent = changetype<TupleArrayEvent>(newMockEvent())
415+
416+
tupleArrayEventEvent.parameters = new Array()
417+
418+
tupleArrayEventEvent.parameters.push(
419+
new ethereum.EventParam(
420+
"tupleArray",
421+
ethereum.Value.fromTupleArray(tupleArray)
422+
)
423+
)
424+
tupleArrayEventEvent.parameters.push(
425+
new ethereum.EventParam(
426+
"addressArray",
427+
ethereum.Value.fromAddressArray(addressArray)
428+
)
429+
)
430+
431+
return tupleArrayEventEvent
432+
}
308433
"
309434
`;
310435
@@ -317,7 +442,7 @@ exports[`Ethereum subgraph scaffolding > Test Files (for indexing events) 1`] =
317442
beforeAll,
318443
afterAll
319444
} from "matchstick-as/assembly/index"
320-
import { BigInt, Bytes } from "@graphprotocol/graph-ts"
445+
import { BigInt, Bytes, Address } from "@graphprotocol/graph-ts"
321446
import { ExampleEvent } from "../generated/schema"
322447
import { ExampleEvent as ExampleEventEvent } from "../generated/Contract/Contract"
323448
import { handleExampleEvent } from "../src/contract"
@@ -389,8 +514,12 @@ describe("Describe entity assertions", () => {
389514
390515
exports[`Ethereum subgraph scaffolding > Test Files (for indexing events) 2`] = `
391516
"import { newMockEvent } from "matchstick-as"
392-
import { ethereum, BigInt, Bytes } from "@graphprotocol/graph-ts"
393-
import { ExampleEvent, ExampleEvent1 } from "../generated/Contract/Contract"
517+
import { ethereum, BigInt, Bytes, Address } from "@graphprotocol/graph-ts"
518+
import {
519+
ExampleEvent,
520+
ExampleEvent1,
521+
TupleArrayEvent
522+
} from "../generated/Contract/Contract"
394523
395524
export function createExampleEventEvent(
396525
a: BigInt,
@@ -437,5 +566,29 @@ export function createExampleEvent1Event(a: Bytes): ExampleEvent1 {
437566
438567
return exampleEvent1Event
439568
}
569+
570+
export function createTupleArrayEventEvent(
571+
tupleArray: Array<ethereum.Tuple>,
572+
addressArray: Array<Address>
573+
): TupleArrayEvent {
574+
let tupleArrayEventEvent = changetype<TupleArrayEvent>(newMockEvent())
575+
576+
tupleArrayEventEvent.parameters = new Array()
577+
578+
tupleArrayEventEvent.parameters.push(
579+
new ethereum.EventParam(
580+
"tupleArray",
581+
ethereum.Value.fromTupleArray(tupleArray)
582+
)
583+
)
584+
tupleArrayEventEvent.parameters.push(
585+
new ethereum.EventParam(
586+
"addressArray",
587+
ethereum.Value.fromAddressArray(addressArray)
588+
)
589+
)
590+
591+
return tupleArrayEventEvent
592+
}
440593
"
441594
`;

packages/cli/src/scaffold/ethereum.test.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,32 @@ const TEST_CALLABLE_FUNCTIONS = [
5656
},
5757
];
5858

59+
const TEST_TUPLE_ARRAY_EVENT = {
60+
name: 'TupleArrayEvent',
61+
type: 'event',
62+
inputs: [
63+
{
64+
name: 'tupleArray',
65+
type: 'tuple[]',
66+
components: [
67+
{ name: 'field1', type: 'uint256' },
68+
{ name: 'field2', type: 'address' },
69+
],
70+
},
71+
{ name: 'addressArray', type: 'address[]' },
72+
],
73+
};
74+
5975
const TEST_ABI = new ABI(
6076
'Contract',
6177
undefined,
62-
immutable.fromJS([TEST_EVENT, OVERLOADED_EVENT, TEST_CONTRACT, ...TEST_CALLABLE_FUNCTIONS]),
78+
immutable.fromJS([
79+
TEST_EVENT,
80+
OVERLOADED_EVENT,
81+
TEST_TUPLE_ARRAY_EVENT,
82+
TEST_CONTRACT,
83+
...TEST_CALLABLE_FUNCTIONS,
84+
]),
6385
);
6486

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

126+
test('Mapping handles tuple array type conversion', async () => {
127+
expect(await scaffold.generateMapping()).toMatchSnapshot();
128+
});
129+
104130
test('Test Files (default)', async () => {
105131
const files = await scaffoldWithIndexEvents.generateTests();
106132
const testFile = files?.['contract.test.ts'];

0 commit comments

Comments
 (0)