Skip to content
This repository has been archived by the owner on Jan 24, 2022. It is now read-only.

Libraries can link to other libraries with public functions #1252

Merged
merged 15 commits into from
Nov 28, 2019
Merged
Show file tree
Hide file tree
Changes from 11 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
9 changes: 9 additions & 0 deletions packages/cli/contracts/mocks/ImplV1b.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
pragma solidity ^0.5.0;

import "mock-stdlib-libdeps/contracts/GreeterLibWithLibImpl.sol";

contract ImplV1b is GreeterLibWithLibImpl {
function say() public view returns(uint256) {
return state.getNumber();
}
}
2 changes: 2 additions & 0 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@
"solc-wrapper": "^0.5.8",
"solidity-parser-antlr": "^0.4.2",
"spinnies": "^0.3.0",
"topological-sort": "^0.3.0",
ylv-io marked this conversation as resolved.
Show resolved Hide resolved
"truffle-config": "1.1.16",
"underscore": "^1.9.1",
"uuid": "^3.3.3",
Expand Down Expand Up @@ -147,6 +148,7 @@
"lodash.random": "^3.2.0",
"mock-stdlib": "file:./test/mocks/mock-stdlib",
"mock-stdlib-2": "file:./test/mocks/mock-stdlib-2",
"mock-stdlib-libdeps": "file:./test/mocks/mock-stdlib-libdeps",
"mock-stdlib-invalid": "file:./test/mocks/mock-stdlib-invalid",
"mock-stdlib-undeployed": "file:./test/mocks/mock-stdlib-undeployed",
"mock-stdlib-undeployed-2": "file:./test/mocks/mock-stdlib-undeployed-2",
Expand Down
43 changes: 35 additions & 8 deletions packages/cli/src/models/network/NetworkController.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
'use strict';

import isEmpty from 'lodash.isempty';
import compact from 'lodash.compact';
import intersection from 'lodash.intersection';
import uniq from 'lodash.uniq';
import flatten from 'lodash.flatten';
import filter from 'lodash.filter';
import every from 'lodash.every';
import partition from 'lodash.partition';
import map from 'lodash.map';
import concat from 'lodash.concat';
import toPairs from 'lodash.topairs';
import TopologicalSort from 'topological-sort';

import {
Contracts,
Expand Down Expand Up @@ -39,6 +38,7 @@ import {
AppProxyMigrator,
MinimalProxy,
} from '@openzeppelin/upgrades';

import { isMigratableManifestVersion } from '../files/ManifestVersion';
import { allPromisesOrError } from '../../utils/async';
import { toContractFullName } from '../../utils/naming';
Expand Down Expand Up @@ -208,7 +208,8 @@ export default class NetworkController {
// Contract model || SolidityLib model
private _solidityLibsForPush(onlyChanged: boolean = false): Contract[] | never {
const { contractNames, contractAliases } = this.projectFile;
const libNames = this._getAllSolidityLibNames(contractNames);

let libNames = this._getAllSolidityLibNames(contractNames);

const clashes = intersection(libNames, contractAliases);
if (!isEmpty(clashes)) {
Expand All @@ -226,12 +227,16 @@ export default class NetworkController {

// Contract model || SolidityLib model
public async uploadSolidityLibs(libs: Contract[]): Promise<void> {
await allPromisesOrError(libs.map(lib => this._uploadSolidityLib(lib)));
// Libs may have dependencies, so deploy them in order
for (let i = 0; i < libs.length; i++) {
await this._uploadSolidityLib(libs[i]);
ylv-io marked this conversation as resolved.
Show resolved Hide resolved
}
}

// Contract model || SolidityLib model
private async _uploadSolidityLib(libClass: Contract): Promise<void> {
const libName = libClass.schema.contractName;
await this._setSolidityLibs(libClass);
ylv-io marked this conversation as resolved.
Show resolved Hide resolved
Loggy.spin(__filename, '_uploadSolidityLib', `upload-solidity-lib${libName}`, `Uploading ${libName} library`);
const libInstance = await this.project.setImplementation(libClass, libName);
this.networkFile.addSolidityLib(libName, libInstance);
Expand Down Expand Up @@ -313,12 +318,34 @@ export default class NetworkController {

// Contract model || SolidityLib model
private _getAllSolidityLibNames(contractNames: string[]): string[] {
const libNames = contractNames.map(contractName => {
const contract = Contracts.getFromLocal(contractName);
return getSolidityLibNames(contract.schema.bytecode);
const graph = new TopologicalSort<string, string>(new Map<string, string>());

contractNames.forEach(contractName => {
const deps = this._getContractDependencies(contractName);
deps.forEach(dependencyContractName => {
this._populateDependencyGraph(dependencyContractName, graph);
});
});

return uniq(flatten(libNames));
const sorted = graph.sort();
return [...sorted.keys()].reverse();
}

private _populateDependencyGraph(contractName: string, graph: TopologicalSort<string, string>) {
// @ts-ignore
if (!graph.nodes.has(contractName)) {
graph.addNode(contractName, contractName);
const deps = this._getContractDependencies(contractName);
deps.forEach(dependencyContractName => {
this._populateDependencyGraph(dependencyContractName, graph);
graph.addEdge(contractName, dependencyContractName);
});
}
}

private _getContractDependencies(contractName: string): string[] {
const contract = Contracts.getFromLocal(contractName);
return getSolidityLibNames(contract.schema.bytecode);
}

// Contract model
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
pragma solidity ^0.5.0;

library GreeterLibLib {
struct StateState {
uint256 aNumber;
}

function getANumber(StateState storage self) public view returns (uint256) {
return self.aNumber;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
pragma solidity ^0.5.0;

import "./GreeterLibLib.sol";

library GreeterLibWithLib {
using GreeterLibLib for GreeterLibLib.StateState;

struct State {
GreeterLibLib.StateState state;
}

function getNumber(State storage self) public view returns (uint256) {
return self.state.getANumber();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
pragma solidity ^0.5.0;

import "./GreeterLibWithLib.sol";

contract GreeterLibWithLibImpl {
using GreeterLibWithLib for GreeterLibWithLib.State;

GreeterLibWithLib.State state;

function greeting() public view returns (uint256) {
return state.getNumber();
}

function version() public pure returns (string memory) {
return "1.1.0";
}
}
12 changes: 12 additions & 0 deletions packages/cli/test/mocks/mock-stdlib-libdeps/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "mock-stdlib-libdeps",
"version": "1.1.0",
"description": "Mock stdlib for tests in zos-cli",
"main": "index.js",
"private": true,
"scripts": {
"test": "truffle test"
},
"author": "[email protected]",
"license": "MIT"
}
4 changes: 4 additions & 0 deletions packages/cli/test/mocks/mock-stdlib-libdeps/truffle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = {
// See <http://truffleframework.com/docs/advanced/configuration>
// to customize your Truffle configuration!
};
8 changes: 8 additions & 0 deletions packages/cli/test/mocks/mock-stdlib-libdeps/zos.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "mock-stdlib-libdeps",
"manifestVersion": "2.2",
"version": "1.1.0",
"contracts": {
"GreeterLibWithLibImpl": "GreeterLibWithLibImpl"
}
}
16 changes: 16 additions & 0 deletions packages/cli/test/mocks/mock-stdlib-libdeps/zos.test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"version": "1.1.0",
"manifestVersion": "2.2",
"provider": {
"address": "0x0000000000000000000000000000000000000010"
},
"package": {
"address": "0x0000000000000000000000000000000000000080"
},
"contracts": {
"GreeterLibWithLibImpl": {
"address": "0x1020",
"bytecodeHash": "0x0001"
}
}
}
Loading