UNPKG

@graphprotocol/graph-cli

Version:

CLI for building for and deploying to The Graph

334 lines (310 loc) • 10.2 kB
const fs = require('fs-extra') const path = require('path') const ABI = require('../abi') const ts = require('../../../codegen/typescript') const AbiCodeGenerator = require('./abi') let tempdir let abi let generatedTypes describe('ABI code generation', () => { beforeAll(async () => { tempdir = await fs.mkdtemp('abi-codegen') try { let filename = path.join(tempdir, 'ABI.json') await fs.writeFile( filename, JSON.stringify([ { constant: true, inputs: [], name: 'read', outputs: [{ name: '', type: 'bytes32' }], payable: false, type: 'function', }, { constant: true, inputs: [ { name: 'proposalId', type: 'uint256', }, { components: [ { name: 'foo', type: 'uint8', }, { name: 'bar', type: 'tuple', components: [{ name: 'baz', type: 'address' }], }, ], name: '', type: 'tuple', }, ], name: 'getProposal', outputs: [ { components: [ { name: 'result', type: 'uint8', }, { name: 'target', type: 'address', }, { name: 'data', type: 'bytes', }, { name: 'proposer', type: 'address', }, { name: 'feeRecipient', type: 'address', }, { name: 'fee', type: 'uint256', }, { name: 'startTime', type: 'uint256', }, { name: 'yesCount', type: 'uint256', }, { name: 'noCount', type: 'uint256', }, ], name: '', type: 'tuple', }, ], payable: false, stateMutability: 'view', type: 'function', }, { type: 'function', stateMutability: 'view', payable: 'false', name: 'getProposals', outputs: [ { type: 'uint256', name: 'size', }, { type: 'tuple[]', components: [ { name: 'first', type: 'uint256' }, { name: 'second', type: 'string' }, ], }, ], }, { type: 'function', stateMutability: 'view', name: 'overloaded', inputs: [ { type: 'string', }, ], outputs: [ { type: 'string', }, ], }, { type: 'function', stateMutability: 'view', name: 'overloaded', inputs: [ { type: 'uint256', }, ], outputs: [ { type: 'string', }, ], }, { type: 'function', stateMutability: 'view', name: 'overloaded', inputs: [ { type: 'bytes32', }, ], outputs: [ { type: 'string', }, ], }, ]), 'utf-8', ) abi = ABI.load('Contract', filename) let codegen = new AbiCodeGenerator(abi) generatedTypes = codegen.generateTypes() } finally { await fs.remove(tempdir) } }) afterAll(async () => { await fs.remove(tempdir) }) describe('Generated types', () => { test('All expected types are generated', () => { expect(generatedTypes.map(type => type.name)).toEqual([ 'Contract__getProposalResultValue0Struct', 'Contract__getProposalInputParam1Struct', 'Contract__getProposalInputParam1BarStruct', 'Contract__getProposalsResultValue1Struct', 'Contract__getProposalsResult', 'Contract', ]) }) }) describe('Contract class', () => { test('Exists', () => { expect(generatedTypes.find(type => type.name === 'Contract')).toBeDefined() }) test('Has methods', () => { let contract = generatedTypes.find(type => type.name === 'Contract') expect(contract.methods).toBeInstanceOf(Array) }) test('Has `bind` method', () => { let contract = generatedTypes.find(type => type.name === 'Contract') expect(contract.methods.find(method => method.name === 'bind')).toBeDefined() }) test('Has methods for all callable functions', () => { let contract = generatedTypes.find(type => type.name === 'Contract') expect(contract.methods.map(method => method.name)).toContain('getProposal') }) }) describe('Methods for callable functions', () => { test('Have correct parameters', () => { let contract = generatedTypes.find(type => type.name === 'Contract') expect(contract.methods.map(method => [method.name, method.params])).toEqual([ ['bind', [ts.param('address', 'Address')]], ['read', []], ['try_read', []], [ 'getProposal', [ ts.param('proposalId', 'BigInt'), ts.param('param1', 'Contract__getProposalInputParam1Struct'), ], ], [ 'try_getProposal', [ ts.param('proposalId', 'BigInt'), ts.param('param1', 'Contract__getProposalInputParam1Struct'), ], ], ['getProposals', []], ['try_getProposals', []], ['overloaded', [ts.param('param0', 'string')]], ['try_overloaded', [ts.param('param0', 'string')]], ['overloaded1', [ts.param('param0', 'BigInt')]], ['try_overloaded1', [ts.param('param0', 'BigInt')]], ['overloaded2', [ts.param('param0', 'Bytes')]], ['try_overloaded2', [ts.param('param0', 'Bytes')]], ]) }) test('Have correct return types', () => { let contract = generatedTypes.find(type => type.name === 'Contract') expect(contract.methods.map(method => [method.name, method.returnType])).toEqual([ ['bind', ts.namedType('Contract')], ['read', ts.namedType('Bytes')], ['try_read', 'ethereum.CallResult<Bytes>'], ['getProposal', ts.namedType('Contract__getProposalResultValue0Struct')], [ 'try_getProposal', 'ethereum.CallResult<Contract__getProposalResultValue0Struct>', ], ['getProposals', ts.namedType('Contract__getProposalsResult')], ['try_getProposals', 'ethereum.CallResult<Contract__getProposalsResult>'], ['overloaded', ts.namedType('string')], ['try_overloaded', 'ethereum.CallResult<string>'], ['overloaded1', ts.namedType('string')], ['try_overloaded1', 'ethereum.CallResult<string>'], ['overloaded2', ts.namedType('string')], ['try_overloaded2', 'ethereum.CallResult<string>'], ]) }) }) describe('Tuples', () => { test('Tuple types exist for function parameters', () => { let tupleType = generatedTypes.find( type => type.name === 'Contract__getProposalInputParam1Struct', ) // Verify that the tuple type has methods expect(tupleType.methods).toBeDefined() // Verify that the tuple type has getters for all tuple fields with // the right return types expect(tupleType.methods.map(method => [method.name, method.returnType])).toEqual([ ['get foo', 'i32'], ['get bar', 'Contract__getProposalInputParam1BarStruct'], ]) // Inner tuple: tupleType = generatedTypes.find( type => type.name === 'Contract__getProposalInputParam1BarStruct', ) // Verify that the tuple type has methods expect(tupleType.methods).toBeDefined() // Verify that the tuple type has getters for all tuple fields with // the right return types expect(tupleType.methods.map(method => [method.name, method.returnType])).toEqual([ ['get baz', 'Address'], ]) }) test('Tuple types exist for function return values', () => { let tupleType = generatedTypes.find( type => type.name === 'Contract__getProposalResultValue0Struct', ) // Verify that the tuple type has methods expect(tupleType.methods).toBeDefined() // Verify that the tuple type has getters for all tuple fields with // the right return types expect(tupleType.methods.map(method => [method.name, method.returnType])).toEqual([ ['get result', 'i32'], ['get target', 'Address'], ['get data', 'Bytes'], ['get proposer', 'Address'], ['get feeRecipient', 'Address'], ['get fee', 'BigInt'], ['get startTime', 'BigInt'], ['get yesCount', 'BigInt'], ['get noCount', 'BigInt'], ]) }) test('Function bodies are generated correctly for tuple arrays', () => { let contract = generatedTypes.find(type => type.name === 'Contract') let getter = contract.methods.find(method => method.name === 'getProposals') expect(getter.body).not.toContain('toTupleArray<undefined>') expect(getter.body).toContain( 'result[1].toTupleArray<Contract__getProposalsResultValue1Struct>()', ) }) }) })