diff --git a/CHANGELOG.md b/CHANGELOG.md index 0580488c..0f9b0199 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ All notable changes will be documented in this file. Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file. +## 12.0.0 + - [Breaking change: Implementation of topological sort to sort custom types by their type dependencies (change attribute visibility)](https://github.com/ElrondNetwork/elrond-sdk-erdjs/pull/224) + ## 11.0.0 - [Breaking change: Sender is now mandatory when constructing a transaction](https://github.com/ElrondNetwork/elrond-sdk-erdjs/pull/TBD) - Switched to MIT license diff --git a/package.json b/package.json index 0e9a4c20..146ff5ec 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@elrondnetwork/erdjs", - "version": "11.0.0", + "version": "12.0.0", "description": "Smart Contracts interaction framework", "main": "out/index.js", "types": "out/index.d.js", diff --git a/src/smartcontracts/typesystem/abiRegistry.ts b/src/smartcontracts/typesystem/abiRegistry.ts index ecb3377b..7bdded62 100644 --- a/src/smartcontracts/typesystem/abiRegistry.ts +++ b/src/smartcontracts/typesystem/abiRegistry.ts @@ -9,15 +9,15 @@ import { EndpointDefinition, EndpointParameterDefinition } from "./endpoint"; export class AbiRegistry { readonly interfaces: ContractInterface[] = []; - readonly customTypes: CustomType[] = []; + private customTypes: CustomType[] = []; - static create(json: { name: string; endpoints: any[]; types: any[] }): AbiRegistry { + static create(json: { name: string; endpoints: any[]; types: Record }): AbiRegistry { let registry = new AbiRegistry().extend(json); let remappedRegistry = registry.remapToKnownTypes(); return remappedRegistry; } - private extend(json: { name: string; endpoints: any[]; types: any[] }): AbiRegistry { + private extend(json: { name: string; endpoints: any[]; types: Record }): AbiRegistry { json.types = json.types || {}; // The "endpoints" collection is interpreted by "ContractInterface". @@ -49,17 +49,37 @@ export class AbiRegistry { } private sortCustomTypesByDependencies() { - // TODO: Improve consistency of the sorting function (and make sure the sorting is stable): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort - this.customTypes.sort((a: CustomType, b: CustomType) => { - const bDependsOnA = b.getNamesOfDependencies().indexOf(a.getName()) > -1; - if (bDependsOnA) { - // Sort "a" before "b". - return -1; + // Use of topological sort algorithm to sort custom types by dependencies. + let dependencies: { [key: string]: string[] } = {}; + let visited: { [key: string]: boolean } = {}; + this.customTypes.forEach((type: CustomType) => { + dependencies[type.getName()] = type.getNamesOfDependencies(); + visited[type.getName()] = false; + }); + let sortedArray = new Array(); + + const topologicalSortUtil = (name: string, visited: { [key: string]: boolean }, sortedArray: CustomType[]) => { + visited[name] = true; + + for (const dependency of dependencies[name]) { + if (!this.customTypes.find((e) => e.getName() == dependency)) continue; + if (!visited[dependency]) { + topologicalSortUtil(dependency, visited, sortedArray); + } } + const type = this.customTypes.find((e) => e.getName() == name); + if (type) { + sortedArray.push(type); + } + }; - // Sort "b" before "a". - return 1; - }); + for (const type of this.customTypes) { + if (!visited[type.getName()]) { + topologicalSortUtil(type.getName(), visited, sortedArray); + } + } + + this.customTypes = sortedArray; } getInterface(name: string): ContractInterface { diff --git a/src/testdata/custom-types-out-of-order.abi.json b/src/testdata/custom-types-out-of-order.abi.json index aa407c32..899cc0ae 100644 --- a/src/testdata/custom-types-out-of-order.abi.json +++ b/src/testdata/custom-types-out-of-order.abi.json @@ -22,6 +22,15 @@ } ] }, + "TypeC": { + "type": "struct", + "fields": [ + { + "name": "foobar", + "type": "u64" + } + ] + }, "EsdtTokenType": { "type": "enum", "variants": [ @@ -35,15 +44,6 @@ } ] }, - "TypeA": { - "type": "struct", - "fields": [ - { - "name": "b", - "type": "TypeB" - } - ] - }, "TypeB": { "type": "struct", "fields": [ @@ -53,12 +53,12 @@ } ] }, - "TypeC": { + "TypeA": { "type": "struct", "fields": [ { - "name": "foobar", - "type": "u64" + "name": "b", + "type": "TypeB" } ] }