thirdweb 0.2.8 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -96
- package/dist/cli/index.js +5 -5
- package/dist/cli/index.js.map +1 -1
- package/package.json +8 -3
- package/src/cli/index.ts +17 -15
- package/src/common/error.ts +1 -191
- package/src/core/builder/brownie.ts +66 -0
- package/src/core/builder/build.ts +21 -1
- package/src/core/builder/builder-base.ts +14 -7
- package/src/core/builder/foundry.ts +4 -10
- package/src/core/builder/hardhat.ts +9 -14
- package/src/core/builder/truffle.ts +65 -0
- package/src/core/detection/brownie.ts +13 -0
- package/src/core/detection/detect.ts +36 -6
- package/src/core/detection/detector.ts +1 -1
- package/src/core/detection/foundry.ts +2 -2
- package/src/core/detection/hardhat.ts +2 -2
- package/src/core/detection/truffle.ts +13 -0
- package/src/core/helpers/storage.ts +3 -3
- package/src/core/interfaces/IStorage.ts +4 -4
- package/src/core/storage/ipfs-storage.ts +21 -15
- package/src/core/types/ProjectType.ts +6 -1
package/src/common/error.ts
CHANGED
@@ -1,49 +1,3 @@
|
|
1
|
-
/**
|
2
|
-
* Error that may get thrown if IPFS returns nothing for a given uri.
|
3
|
-
* @internal
|
4
|
-
*/
|
5
|
-
export class NotFoundError extends Error {
|
6
|
-
/** @internal */
|
7
|
-
constructor(identifier?: string) {
|
8
|
-
super(identifier ? `Object with id ${identifier} NOT FOUND` : "NOT_FOUND");
|
9
|
-
}
|
10
|
-
}
|
11
|
-
|
12
|
-
/**
|
13
|
-
* Error that may get thrown if an invalid address was passed
|
14
|
-
* @internal
|
15
|
-
*/
|
16
|
-
export class InvalidAddressError extends Error {
|
17
|
-
/** @internal */
|
18
|
-
constructor(address?: string) {
|
19
|
-
super(
|
20
|
-
address ? `'${address}' is an invalid address` : "Invalid address passed"
|
21
|
-
);
|
22
|
-
}
|
23
|
-
}
|
24
|
-
|
25
|
-
/**
|
26
|
-
* @internal
|
27
|
-
*/
|
28
|
-
export class MissingRoleError extends Error {
|
29
|
-
/** @internal */
|
30
|
-
/** @internal */
|
31
|
-
constructor(address: string, role: string) {
|
32
|
-
super(`MISSING ROLE: ${address} does not have the '${role}' role`);
|
33
|
-
}
|
34
|
-
}
|
35
|
-
|
36
|
-
/**
|
37
|
-
* @internal
|
38
|
-
*/
|
39
|
-
export class AssetNotFoundError extends Error {
|
40
|
-
/** @internal */
|
41
|
-
/** @internal */
|
42
|
-
constructor(message = "The asset you're trying to use could not be found.") {
|
43
|
-
super(`message: ${message}`);
|
44
|
-
}
|
45
|
-
}
|
46
|
-
|
47
1
|
/**
|
48
2
|
* @internal
|
49
3
|
*/
|
@@ -54,16 +8,6 @@ export class UploadError extends Error {
|
|
54
8
|
}
|
55
9
|
}
|
56
10
|
|
57
|
-
/**
|
58
|
-
* @internal
|
59
|
-
*/
|
60
|
-
export class FileNameMissingError extends Error {
|
61
|
-
/** @internal */
|
62
|
-
constructor() {
|
63
|
-
super("File name is required when object is not a `File` type object.");
|
64
|
-
}
|
65
|
-
}
|
66
|
-
|
67
11
|
/**
|
68
12
|
* @internal
|
69
13
|
*/
|
@@ -71,43 +15,11 @@ export class DuplicateFileNameError extends Error {
|
|
71
15
|
/** @internal */
|
72
16
|
constructor(fileName: string) {
|
73
17
|
super(
|
74
|
-
`DUPLICATE_FILE_NAME_ERROR: File name ${fileName} was passed for more than one file
|
18
|
+
`DUPLICATE_FILE_NAME_ERROR: File name ${fileName} was passed for more than one file.`,
|
75
19
|
);
|
76
20
|
}
|
77
21
|
}
|
78
22
|
|
79
|
-
/**
|
80
|
-
* @internal
|
81
|
-
*/
|
82
|
-
export class NotEnoughTokensError extends Error {
|
83
|
-
/** @internal */
|
84
|
-
constructor(contractAddress: string, quantity: number, available: number) {
|
85
|
-
super(
|
86
|
-
`BALANCE ERROR: you do not have enough balance on contract ${contractAddress} to use ${quantity} tokens. You have ${available} tokens available.`
|
87
|
-
);
|
88
|
-
}
|
89
|
-
}
|
90
|
-
|
91
|
-
/**
|
92
|
-
* @internal
|
93
|
-
*/
|
94
|
-
export class MissingOwnerRoleError extends Error {
|
95
|
-
/** @internal */
|
96
|
-
constructor() {
|
97
|
-
super(`LIST ERROR: you should be the owner of the token to list it.`);
|
98
|
-
}
|
99
|
-
}
|
100
|
-
|
101
|
-
/**
|
102
|
-
* @internal
|
103
|
-
*/
|
104
|
-
export class QuantityAboveLimitError extends Error {
|
105
|
-
/** @internal */
|
106
|
-
constructor(quantity: string) {
|
107
|
-
super(`BUY ERROR: You cannot buy more than ${quantity} tokens`);
|
108
|
-
}
|
109
|
-
}
|
110
|
-
|
111
23
|
/**
|
112
24
|
* Thrown when data fails to fetch from storage.
|
113
25
|
* @internal
|
@@ -121,105 +33,3 @@ export class FetchError extends Error {
|
|
121
33
|
this.innerError = innerError;
|
122
34
|
}
|
123
35
|
}
|
124
|
-
|
125
|
-
/**
|
126
|
-
* Thrown when attempting to create a snapshot with duplicate leafs
|
127
|
-
* @internal
|
128
|
-
*/
|
129
|
-
export class DuplicateLeafsError extends Error {
|
130
|
-
constructor(message?: string) {
|
131
|
-
super(`DUPLICATE_LEAFS${message ? ` : ${message}` : ""}`);
|
132
|
-
}
|
133
|
-
}
|
134
|
-
|
135
|
-
/**
|
136
|
-
* Thrown when attempting to update/cancel an auction that already started
|
137
|
-
* @internal
|
138
|
-
*/
|
139
|
-
export class AuctionAlreadyStartedError extends Error {
|
140
|
-
constructor(id?: string) {
|
141
|
-
super(
|
142
|
-
`Auction already started with existing bid${id ? `, id: ${id}` : ""}`
|
143
|
-
);
|
144
|
-
}
|
145
|
-
}
|
146
|
-
|
147
|
-
/**
|
148
|
-
* @internal
|
149
|
-
*/
|
150
|
-
export class FunctionDeprecatedError extends Error {
|
151
|
-
/** @internal */
|
152
|
-
constructor(message: string) {
|
153
|
-
super(`FUNCTION DEPRECATED. ${message ? `Use ${message} instead` : ""}`);
|
154
|
-
}
|
155
|
-
}
|
156
|
-
/**
|
157
|
-
* Thrown when trying to retrieve a listing from a marketplace that doesn't exist
|
158
|
-
* @internal
|
159
|
-
*/
|
160
|
-
export class ListingNotFoundError extends Error {
|
161
|
-
constructor(marketplaceContractAddress: string, listingId?: string) {
|
162
|
-
super(
|
163
|
-
`Could not find listing.${
|
164
|
-
marketplaceContractAddress
|
165
|
-
? ` marketplace address: ${marketplaceContractAddress}`
|
166
|
-
: ""
|
167
|
-
}${listingId ? ` listing id: ${listingId}` : ""}`
|
168
|
-
);
|
169
|
-
}
|
170
|
-
}
|
171
|
-
|
172
|
-
/**
|
173
|
-
* Thrown when trying to retrieve a listing of the wrong type
|
174
|
-
* @internal
|
175
|
-
*/
|
176
|
-
export class WrongListingTypeError extends Error {
|
177
|
-
constructor(
|
178
|
-
marketplaceContractAddress: string,
|
179
|
-
listingId?: string,
|
180
|
-
actualType?: string,
|
181
|
-
expectedType?: string
|
182
|
-
) {
|
183
|
-
super(
|
184
|
-
`Incorrect listing type. Are you sure you're using the right method?.${
|
185
|
-
marketplaceContractAddress
|
186
|
-
? ` marketplace address: ${marketplaceContractAddress}`
|
187
|
-
: ""
|
188
|
-
}${listingId ? ` listing id: ${listingId}` : ""}${
|
189
|
-
expectedType ? ` expected type: ${expectedType}` : ""
|
190
|
-
}${actualType ? ` actual type: ${actualType}` : ""}`
|
191
|
-
);
|
192
|
-
}
|
193
|
-
}
|
194
|
-
|
195
|
-
/**
|
196
|
-
* Thrown when attempting to transfer an asset that has restricted transferability
|
197
|
-
* @internal
|
198
|
-
*/
|
199
|
-
export class RestrictedTransferError extends Error {
|
200
|
-
constructor(assetAddress?: string) {
|
201
|
-
super(
|
202
|
-
`Failed to transfer asset, transfer is restricted.${
|
203
|
-
assetAddress ? ` Address : ${assetAddress}` : ""
|
204
|
-
}`
|
205
|
-
);
|
206
|
-
}
|
207
|
-
}
|
208
|
-
|
209
|
-
/**
|
210
|
-
* Thrown when attempting to execute an admin-role function.
|
211
|
-
* @internal
|
212
|
-
*/
|
213
|
-
export class AdminRoleMissingError extends Error {
|
214
|
-
constructor(
|
215
|
-
address?: string,
|
216
|
-
contractAddress?: string,
|
217
|
-
message = "Failed to execute transaction"
|
218
|
-
) {
|
219
|
-
super(
|
220
|
-
`${message}, admin role is missing${
|
221
|
-
address ? ` on address: ${address}` : ""
|
222
|
-
}${contractAddress ? ` on contract: ${contractAddress}` : ""}`
|
223
|
-
);
|
224
|
-
}
|
225
|
-
}
|
@@ -0,0 +1,66 @@
|
|
1
|
+
import { logger } from "../helpers/logger";
|
2
|
+
import { CompileOptions } from "../interfaces/Builder";
|
3
|
+
import { ContractPayload } from "../interfaces/ContractPayload";
|
4
|
+
import { BaseBuilder } from "./builder-base";
|
5
|
+
import { execSync } from "child_process";
|
6
|
+
import { existsSync, readFileSync, rmdirSync } from "fs";
|
7
|
+
import { basename, join } from "path";
|
8
|
+
import { parse } from "yaml";
|
9
|
+
|
10
|
+
export class BrownieBuilder extends BaseBuilder {
|
11
|
+
public async compile(options: CompileOptions): Promise<{
|
12
|
+
contracts: ContractPayload[];
|
13
|
+
}> {
|
14
|
+
const config = parse(
|
15
|
+
readFileSync(join(options.projectPath, "brownie-config.yaml"), "utf-8"),
|
16
|
+
);
|
17
|
+
|
18
|
+
const buildPath = join(
|
19
|
+
options.projectPath,
|
20
|
+
config?.project_structure?.build || "./build",
|
21
|
+
);
|
22
|
+
|
23
|
+
if (options.clean) {
|
24
|
+
logger.info("Cleaning build directory");
|
25
|
+
existsSync(buildPath) && rmdirSync(buildPath, { recursive: true });
|
26
|
+
}
|
27
|
+
|
28
|
+
logger.info("Compiling...");
|
29
|
+
execSync("brownie compile");
|
30
|
+
|
31
|
+
const contractsPath = join(buildPath, "contracts/");
|
32
|
+
|
33
|
+
const contracts: ContractPayload[] = [];
|
34
|
+
const files: string[] = [];
|
35
|
+
this.findFiles(contractsPath, /^.*(?<!dbg)\.json$/, files);
|
36
|
+
|
37
|
+
for (const file of files) {
|
38
|
+
logger.debug("Processing:", file.replace(contractsPath, ""));
|
39
|
+
const contractName = basename(file, ".json");
|
40
|
+
const contractJsonFile = readFileSync(file, "utf-8");
|
41
|
+
|
42
|
+
const contractInfo = JSON.parse(contractJsonFile);
|
43
|
+
const abi = contractInfo.abi;
|
44
|
+
const bytecode = contractInfo.bytecode;
|
45
|
+
|
46
|
+
for (const input of abi) {
|
47
|
+
if (this.isThirdwebContract(input)) {
|
48
|
+
if (contracts.find((c) => c.name === contractName)) {
|
49
|
+
logger.error(
|
50
|
+
`Found multiple contracts with name "${contractName}". Contract names should be unique.`,
|
51
|
+
);
|
52
|
+
process.exit(1);
|
53
|
+
}
|
54
|
+
contracts.push({
|
55
|
+
abi,
|
56
|
+
bytecode,
|
57
|
+
name: contractName,
|
58
|
+
});
|
59
|
+
break;
|
60
|
+
}
|
61
|
+
}
|
62
|
+
}
|
63
|
+
|
64
|
+
return { contracts };
|
65
|
+
}
|
66
|
+
}
|
@@ -1,12 +1,14 @@
|
|
1
1
|
import { ContractPayload } from "../interfaces/ContractPayload";
|
2
2
|
import { ProjectType } from "./../types/ProjectType";
|
3
|
+
import { BrownieBuilder } from "./brownie";
|
3
4
|
import { FoundryBuilder } from "./foundry";
|
4
5
|
import { HardhatBuilder } from "./hardhat";
|
6
|
+
import { TruffleBuilder } from "./truffle";
|
5
7
|
|
6
8
|
export default async function build(
|
7
9
|
path: string,
|
8
10
|
projectType: ProjectType,
|
9
|
-
clean: boolean
|
11
|
+
clean: boolean,
|
10
12
|
): Promise<{
|
11
13
|
contracts: ContractPayload[];
|
12
14
|
}> {
|
@@ -29,6 +31,24 @@ export default async function build(
|
|
29
31
|
});
|
30
32
|
}
|
31
33
|
|
34
|
+
case "truffle": {
|
35
|
+
const builder = new TruffleBuilder();
|
36
|
+
return await builder.compile({
|
37
|
+
name: "",
|
38
|
+
projectPath: path,
|
39
|
+
clean,
|
40
|
+
});
|
41
|
+
}
|
42
|
+
|
43
|
+
case "brownie": {
|
44
|
+
const builder = new BrownieBuilder();
|
45
|
+
return await builder.compile({
|
46
|
+
name: "",
|
47
|
+
projectPath: path,
|
48
|
+
clean,
|
49
|
+
});
|
50
|
+
}
|
51
|
+
|
32
52
|
default: {
|
33
53
|
throw new Error("Unknown project type");
|
34
54
|
}
|
@@ -1,11 +1,12 @@
|
|
1
|
-
import {
|
2
|
-
import { basename, join } from "path";
|
1
|
+
import { logger } from "../helpers/logger";
|
3
2
|
import { CompileOptions, IBuilder } from "../interfaces/Builder";
|
4
3
|
import { ContractPayload } from "../interfaces/ContractPayload";
|
4
|
+
import { existsSync, readdirSync, statSync } from "fs";
|
5
|
+
import { basename, join } from "path";
|
5
6
|
|
6
7
|
export abstract class BaseBuilder implements IBuilder {
|
7
8
|
abstract compile(
|
8
|
-
options: CompileOptions
|
9
|
+
options: CompileOptions,
|
9
10
|
): Promise<{ contracts: ContractPayload[] }>;
|
10
11
|
|
11
12
|
protected isThirdwebContract(input: any): boolean {
|
@@ -25,14 +26,20 @@ export abstract class BaseBuilder implements IBuilder {
|
|
25
26
|
return;
|
26
27
|
}
|
27
28
|
|
28
|
-
|
29
|
-
for (
|
30
|
-
|
29
|
+
const files = readdirSync(startPath);
|
30
|
+
for (let i = 0; i < files.length; i++) {
|
31
|
+
const filename = join(startPath, files[i]);
|
31
32
|
//skip the actual thirdweb contract itself
|
32
33
|
if (basename(filename, ".json") === "ThirdwebContract") {
|
33
34
|
continue;
|
34
35
|
}
|
35
|
-
|
36
|
+
const stat = statSync(filename);
|
37
|
+
|
38
|
+
// brownie has a "depdendencies" directory *inside* the build directory, if we detect that we should skip it
|
39
|
+
if (stat.isDirectory() && basename(filename) === "dependencies") {
|
40
|
+
logger.debug('skipping "dependencies" directory');
|
41
|
+
continue;
|
42
|
+
}
|
36
43
|
if (stat.isDirectory()) {
|
37
44
|
this.findFiles(filename, filter, results);
|
38
45
|
} else if (filter.test(filename)) {
|
@@ -1,11 +1,10 @@
|
|
1
|
-
import { execSync } from "child_process";
|
2
|
-
import { readFileSync } from "fs";
|
3
|
-
import { basename, join } from "path";
|
4
1
|
import { logger } from "../helpers/logger";
|
5
2
|
import { CompileOptions } from "../interfaces/Builder";
|
6
3
|
import { ContractPayload } from "../interfaces/ContractPayload";
|
7
|
-
|
8
4
|
import { BaseBuilder } from "./builder-base";
|
5
|
+
import { execSync } from "child_process";
|
6
|
+
import { readFileSync } from "fs";
|
7
|
+
import { basename, join } from "path";
|
9
8
|
|
10
9
|
export class FoundryBuilder extends BaseBuilder {
|
11
10
|
public async compile(options: CompileOptions): Promise<{
|
@@ -44,7 +43,7 @@ export class FoundryBuilder extends BaseBuilder {
|
|
44
43
|
if (this.isThirdwebContract(input)) {
|
45
44
|
if (contracts.find((c) => c.name === contractName)) {
|
46
45
|
logger.error(
|
47
|
-
`Found multiple contracts with name "${contractName}". Contract names should be unique
|
46
|
+
`Found multiple contracts with name "${contractName}". Contract names should be unique.`,
|
48
47
|
);
|
49
48
|
process.exit(1);
|
50
49
|
}
|
@@ -58,11 +57,6 @@ export class FoundryBuilder extends BaseBuilder {
|
|
58
57
|
}
|
59
58
|
}
|
60
59
|
|
61
|
-
logger.info(
|
62
|
-
"Detected thirdweb contracts:",
|
63
|
-
contracts.map((c) => c.name).join(", ")
|
64
|
-
);
|
65
|
-
|
66
60
|
return { contracts };
|
67
61
|
}
|
68
62
|
}
|
@@ -1,11 +1,11 @@
|
|
1
|
-
import { existsSync, readdirSync, readFileSync, statSync } from "fs";
|
2
|
-
import { HardhatConfig } from "hardhat/types";
|
3
|
-
import { basename, join, resolve } from "path";
|
4
1
|
import { logger } from "../helpers/logger";
|
5
2
|
import { CompileOptions } from "../interfaces/Builder";
|
6
3
|
import { ContractPayload } from "../interfaces/ContractPayload";
|
7
|
-
import { execSync } from "child_process";
|
8
4
|
import { BaseBuilder } from "./builder-base";
|
5
|
+
import { execSync } from "child_process";
|
6
|
+
import { existsSync, readFileSync, readdirSync, statSync } from "fs";
|
7
|
+
import { HardhatConfig } from "hardhat/types";
|
8
|
+
import { basename, join, resolve } from "path";
|
9
9
|
|
10
10
|
export class HardhatBuilder extends BaseBuilder {
|
11
11
|
public async compile(options: CompileOptions): Promise<{
|
@@ -23,25 +23,25 @@ export class HardhatBuilder extends BaseBuilder {
|
|
23
23
|
// then we look up the hardhat config extractor file path from there
|
24
24
|
const configExtractorScriptPath = resolve(
|
25
25
|
__dirname,
|
26
|
-
"../helpers/hardhat-config-extractor.js"
|
26
|
+
"../helpers/hardhat-config-extractor.js",
|
27
27
|
);
|
28
28
|
|
29
29
|
//the hardhat extractor **logs out** the runtime config of hardhat, we take that stdout and parse it
|
30
30
|
const stringifiedConfig = execSync(
|
31
|
-
`npx hardhat run ${configExtractorScriptPath} --no-compile
|
31
|
+
`npx hardhat run ${configExtractorScriptPath} --no-compile`,
|
32
32
|
).toString();
|
33
33
|
//voila the hardhat config
|
34
34
|
const actualHardhatConfig = JSON.parse(stringifiedConfig) as HardhatConfig;
|
35
35
|
|
36
36
|
logger.debug(
|
37
37
|
"successfully extracted hardhat config",
|
38
|
-
actualHardhatConfig.paths
|
38
|
+
actualHardhatConfig.paths,
|
39
39
|
);
|
40
40
|
|
41
41
|
const artifactsPath = actualHardhatConfig.paths.artifacts;
|
42
42
|
const sourcesDir = actualHardhatConfig.paths.sources.replace(
|
43
43
|
options.projectPath,
|
44
|
-
""
|
44
|
+
"",
|
45
45
|
);
|
46
46
|
const contractsPath = join(artifactsPath, sourcesDir);
|
47
47
|
|
@@ -62,7 +62,7 @@ export class HardhatBuilder extends BaseBuilder {
|
|
62
62
|
if (this.isThirdwebContract(input)) {
|
63
63
|
if (contracts.find((c) => c.name === contractName)) {
|
64
64
|
logger.error(
|
65
|
-
`Found multiple contracts with name "${contractName}". Contract names should be unique
|
65
|
+
`Found multiple contracts with name "${contractName}". Contract names should be unique.`,
|
66
66
|
);
|
67
67
|
process.exit(1);
|
68
68
|
}
|
@@ -76,11 +76,6 @@ export class HardhatBuilder extends BaseBuilder {
|
|
76
76
|
}
|
77
77
|
}
|
78
78
|
|
79
|
-
logger.info(
|
80
|
-
"Detected thirdweb contracts:",
|
81
|
-
contracts.map((c) => c.name).join(", ")
|
82
|
-
);
|
83
|
-
|
84
79
|
return {
|
85
80
|
contracts,
|
86
81
|
};
|
@@ -0,0 +1,65 @@
|
|
1
|
+
import { logger } from "../helpers/logger";
|
2
|
+
import { CompileOptions } from "../interfaces/Builder";
|
3
|
+
import { ContractPayload } from "../interfaces/ContractPayload";
|
4
|
+
import { BaseBuilder } from "./builder-base";
|
5
|
+
import { execSync } from "child_process";
|
6
|
+
import { existsSync, readFileSync, rmdirSync } from "fs";
|
7
|
+
import { basename, join } from "path";
|
8
|
+
|
9
|
+
export class TruffleBuilder extends BaseBuilder {
|
10
|
+
public async compile(options: CompileOptions): Promise<{
|
11
|
+
contracts: ContractPayload[];
|
12
|
+
}> {
|
13
|
+
// get the current config first
|
14
|
+
const truffleConfig = require(join(
|
15
|
+
options.projectPath,
|
16
|
+
"truffle-config.js",
|
17
|
+
));
|
18
|
+
|
19
|
+
const buildPath = join(
|
20
|
+
options.projectPath,
|
21
|
+
truffleConfig.contracts_build_directory || "./build/contracts",
|
22
|
+
);
|
23
|
+
|
24
|
+
if (options.clean) {
|
25
|
+
logger.info("Cleaning build directory");
|
26
|
+
existsSync(buildPath) && rmdirSync(buildPath, { recursive: true });
|
27
|
+
}
|
28
|
+
|
29
|
+
logger.info("Compiling...");
|
30
|
+
execSync("npx truffle compile");
|
31
|
+
|
32
|
+
const contracts: ContractPayload[] = [];
|
33
|
+
const files: string[] = [];
|
34
|
+
this.findFiles(buildPath, /^.*(?<!dbg)\.json$/, files);
|
35
|
+
|
36
|
+
for (const file of files) {
|
37
|
+
logger.debug("Processing:", file.replace(buildPath, ""));
|
38
|
+
const contractName = basename(file, ".json");
|
39
|
+
const contractJsonFile = readFileSync(file, "utf-8");
|
40
|
+
|
41
|
+
const contractInfo = JSON.parse(contractJsonFile);
|
42
|
+
const abi = contractInfo.abi;
|
43
|
+
const bytecode = contractInfo.bytecode;
|
44
|
+
|
45
|
+
for (const input of abi) {
|
46
|
+
if (this.isThirdwebContract(input)) {
|
47
|
+
if (contracts.find((c) => c.name === contractName)) {
|
48
|
+
logger.error(
|
49
|
+
`Found multiple contracts with name "${contractName}". Contract names should be unique.`,
|
50
|
+
);
|
51
|
+
process.exit(1);
|
52
|
+
}
|
53
|
+
contracts.push({
|
54
|
+
abi,
|
55
|
+
bytecode,
|
56
|
+
name: contractName,
|
57
|
+
});
|
58
|
+
break;
|
59
|
+
}
|
60
|
+
}
|
61
|
+
}
|
62
|
+
|
63
|
+
return { contracts };
|
64
|
+
}
|
65
|
+
}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
import { logger } from "../helpers/logger";
|
2
|
+
import { ProjectType } from "../types/ProjectType";
|
3
|
+
import { Detector } from "./detector";
|
4
|
+
import { existsSync } from "fs";
|
5
|
+
|
6
|
+
export default class BrownieDetector implements Detector {
|
7
|
+
public projectType: ProjectType = "brownie";
|
8
|
+
|
9
|
+
public matches(path: string): boolean {
|
10
|
+
logger.debug("Checking if " + path + " is a Foundry project");
|
11
|
+
return existsSync(path + "/brownie-config.yaml");
|
12
|
+
}
|
13
|
+
}
|
@@ -1,16 +1,46 @@
|
|
1
|
+
import { logger } from "../helpers/logger";
|
1
2
|
import { ProjectType } from "../types/ProjectType";
|
3
|
+
import BrownieDetector from "./brownie";
|
2
4
|
import { Detector } from "./detector";
|
3
5
|
import FoundryDetector from "./foundry";
|
4
6
|
import HardhatDetector from "./hardhat";
|
7
|
+
import TruffleDetector from "./truffle";
|
8
|
+
import inquirer from "inquirer";
|
5
9
|
|
6
10
|
export default async function detect(path: string): Promise<ProjectType> {
|
7
|
-
const detectors: Detector[] = [
|
11
|
+
const detectors: Detector[] = [
|
12
|
+
new HardhatDetector(),
|
13
|
+
new FoundryDetector(),
|
14
|
+
new TruffleDetector(),
|
15
|
+
new BrownieDetector(),
|
16
|
+
];
|
8
17
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
18
|
+
const possibleProjectTypes = detectors
|
19
|
+
.filter((detector) => detector.matches(path))
|
20
|
+
.map((detector) => detector.projectType);
|
21
|
+
|
22
|
+
//if there is no project returned at all then just return unknown}
|
23
|
+
if (!possibleProjectTypes.length) {
|
24
|
+
return "unknown";
|
25
|
+
}
|
26
|
+
//if there is only one possible option just return it
|
27
|
+
if (possibleProjectTypes.length === 1) {
|
28
|
+
logger.info("Detected project type:", possibleProjectTypes[0]);
|
29
|
+
return possibleProjectTypes[0];
|
13
30
|
}
|
14
31
|
|
15
|
-
|
32
|
+
logger.info(
|
33
|
+
"Detected multiple possible build tools:",
|
34
|
+
possibleProjectTypes.map((s) => `"${s}"`).join(", "),
|
35
|
+
);
|
36
|
+
|
37
|
+
const question = "How would you like to compile your contracts";
|
38
|
+
|
39
|
+
const answer = await inquirer.prompt({
|
40
|
+
type: "list",
|
41
|
+
choices: possibleProjectTypes,
|
42
|
+
name: question,
|
43
|
+
});
|
44
|
+
|
45
|
+
return answer[question];
|
16
46
|
}
|
@@ -1,12 +1,12 @@
|
|
1
|
-
import { existsSync } from "fs";
|
2
1
|
import { logger } from "../helpers/logger";
|
3
2
|
import { ProjectType } from "../types/ProjectType";
|
4
3
|
import { Detector } from "./detector";
|
4
|
+
import { existsSync } from "fs";
|
5
5
|
|
6
6
|
export default class FoundryDetector implements Detector {
|
7
7
|
public projectType: ProjectType = "foundry";
|
8
8
|
|
9
|
-
public
|
9
|
+
public matches(path: string): boolean {
|
10
10
|
logger.debug("Checking if " + path + " is a Foundry project");
|
11
11
|
return existsSync(path + "/foundry.toml");
|
12
12
|
}
|
@@ -1,12 +1,12 @@
|
|
1
|
-
import { existsSync } from "fs";
|
2
1
|
import { logger } from "../helpers/logger";
|
3
2
|
import { ProjectType } from "../types/ProjectType";
|
4
3
|
import { Detector } from "./detector";
|
4
|
+
import { existsSync } from "fs";
|
5
5
|
|
6
6
|
export default class HardhatDetector implements Detector {
|
7
7
|
public projectType: ProjectType = "hardhat";
|
8
8
|
|
9
|
-
public
|
9
|
+
public matches(path: string): boolean {
|
10
10
|
logger.debug("Checking if " + path + " is a Hardhat project");
|
11
11
|
return (
|
12
12
|
existsSync(path + "/hardhat.config.js") ||
|
@@ -0,0 +1,13 @@
|
|
1
|
+
import { logger } from "../helpers/logger";
|
2
|
+
import { ProjectType } from "../types/ProjectType";
|
3
|
+
import { Detector } from "./detector";
|
4
|
+
import { existsSync } from "fs";
|
5
|
+
|
6
|
+
export default class TruffleDetector implements Detector {
|
7
|
+
public projectType: ProjectType = "truffle" as const;
|
8
|
+
|
9
|
+
public matches(path: string): boolean {
|
10
|
+
logger.debug("Checking if " + path + " is a Truffle project");
|
11
|
+
return existsSync(path + "/truffle-config.js");
|
12
|
+
}
|
13
|
+
}
|
@@ -14,7 +14,7 @@ import { Json } from "../types";
|
|
14
14
|
*/
|
15
15
|
export function replaceFilePropertiesWithHashes(
|
16
16
|
object: Record<string, any>,
|
17
|
-
cids: string[]
|
17
|
+
cids: string[],
|
18
18
|
) {
|
19
19
|
const keys = Object.keys(object);
|
20
20
|
for (const key in keys) {
|
@@ -43,7 +43,7 @@ export function replaceFilePropertiesWithHashes(
|
|
43
43
|
export function replaceHashWithGatewayUrl(
|
44
44
|
object: Record<string, any>,
|
45
45
|
scheme: string,
|
46
|
-
gatewayUrl: string
|
46
|
+
gatewayUrl: string,
|
47
47
|
): Record<string, any> {
|
48
48
|
const keys = Object.keys(object);
|
49
49
|
for (const key in keys) {
|
@@ -75,7 +75,7 @@ export function replaceHashWithGatewayUrl(
|
|
75
75
|
export function resolveGatewayUrl<T extends Json>(
|
76
76
|
ipfsHash: T,
|
77
77
|
scheme: string,
|
78
|
-
gatewayUrl: string
|
78
|
+
gatewayUrl: string,
|
79
79
|
): T {
|
80
80
|
if (typeof ipfsHash === "string") {
|
81
81
|
return ipfsHash && ipfsHash.toLowerCase().includes(scheme)
|