thirdweb 5.56.0-nightly-07b949dd8c07ffdeda40a5549c31ad4b09abbbf1-20240916000437 → 5.56.0-nightly-a5e605c65e360a9d3d2e553d6783e58582b50a70-20240917000331
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/dist/cjs/contract/deployment/publisher.js +4 -4
- package/dist/cjs/contract/deployment/publisher.js.map +1 -1
- package/dist/cjs/exports/extensions/thirdweb.js +4 -3
- package/dist/cjs/exports/extensions/thirdweb.js.map +1 -1
- package/dist/cjs/exports/modules.js +4 -1
- package/dist/cjs/exports/modules.js.map +1 -1
- package/dist/cjs/extensions/modules/common/checkModulesCompatibility.js +95 -0
- package/dist/cjs/extensions/modules/common/checkModulesCompatibility.js.map +1 -0
- package/dist/cjs/extensions/thirdweb/write/publish.js +106 -0
- package/dist/cjs/extensions/thirdweb/write/publish.js.map +1 -0
- package/dist/cjs/utils/arrays.js +23 -0
- package/dist/cjs/utils/arrays.js.map +1 -0
- package/dist/cjs/utils/semver.js +67 -0
- package/dist/cjs/utils/semver.js.map +1 -0
- package/dist/cjs/version.js +1 -1
- package/dist/esm/contract/deployment/publisher.js +1 -1
- package/dist/esm/contract/deployment/publisher.js.map +1 -1
- package/dist/esm/exports/extensions/thirdweb.js +1 -1
- package/dist/esm/exports/extensions/thirdweb.js.map +1 -1
- package/dist/esm/exports/modules.js +2 -1
- package/dist/esm/exports/modules.js.map +1 -1
- package/dist/esm/extensions/modules/common/checkModulesCompatibility.js +93 -0
- package/dist/esm/extensions/modules/common/checkModulesCompatibility.js.map +1 -0
- package/dist/esm/extensions/thirdweb/write/publish.js +102 -0
- package/dist/esm/extensions/thirdweb/write/publish.js.map +1 -0
- package/dist/esm/utils/arrays.js +20 -0
- package/dist/esm/utils/arrays.js.map +1 -0
- package/dist/esm/utils/semver.js +62 -0
- package/dist/esm/utils/semver.js.map +1 -0
- package/dist/esm/version.js +1 -1
- package/dist/types/contract/deployment/publisher.d.ts +1 -0
- package/dist/types/contract/deployment/publisher.d.ts.map +1 -1
- package/dist/types/exports/extensions/thirdweb.d.ts +1 -1
- package/dist/types/exports/extensions/thirdweb.d.ts.map +1 -1
- package/dist/types/exports/modules.d.ts +2 -1
- package/dist/types/exports/modules.d.ts.map +1 -1
- package/dist/types/extensions/modules/common/checkModulesCompatibility.d.ts +9 -0
- package/dist/types/extensions/modules/common/checkModulesCompatibility.d.ts.map +1 -0
- package/dist/types/extensions/thirdweb/write/publish.d.ts +29 -0
- package/dist/types/extensions/thirdweb/write/publish.d.ts.map +1 -0
- package/dist/types/utils/any-evm/deploy-metadata.d.ts +18 -1
- package/dist/types/utils/any-evm/deploy-metadata.d.ts.map +1 -1
- package/dist/types/utils/arrays.d.ts +5 -0
- package/dist/types/utils/arrays.d.ts.map +1 -0
- package/dist/types/utils/semver.d.ts +25 -0
- package/dist/types/utils/semver.d.ts.map +1 -0
- package/dist/types/version.d.ts +1 -1
- package/package.json +23 -23
- package/src/contract/deployment/publisher.ts +2 -1
- package/src/exports/extensions/thirdweb.ts +6 -4
- package/src/exports/modules.ts +5 -1
- package/src/extensions/erc721/lazyMinting/write/createAndReveal.test.ts +1 -1
- package/src/extensions/modules/MintableERC1155/mintableERC1155.test.ts +1 -1
- package/src/extensions/modules/MintableERC20/mintableERC20.test.ts +1 -1
- package/src/extensions/modules/MintableERC721/mintableERC721.test.ts +1 -1
- package/src/extensions/modules/common/checkModulesCompatibility.test.ts +46 -0
- package/src/extensions/modules/common/checkModulesCompatibility.ts +123 -0
- package/src/extensions/thirdweb/write/publish.test.ts +162 -0
- package/src/extensions/thirdweb/write/publish.ts +131 -0
- package/src/utils/any-evm/deploy-metadata.ts +18 -4
- package/src/utils/arrays.ts +23 -0
- package/src/utils/semver.ts +76 -0
- package/src/version.ts +1 -1
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "thirdweb",
|
3
|
-
"version": "5.56.0-nightly-
|
3
|
+
"version": "5.56.0-nightly-a5e605c65e360a9d3d2e553d6783e58582b50a70-20240917000331",
|
4
4
|
"repository": {
|
5
5
|
"type": "git",
|
6
6
|
"url": "git+https://github.com/thirdweb-dev/js.git#main"
|
@@ -210,7 +210,7 @@
|
|
210
210
|
"@radix-ui/react-focus-scope": "1.1.0",
|
211
211
|
"@radix-ui/react-icons": "1.3.0",
|
212
212
|
"@radix-ui/react-tooltip": "1.1.2",
|
213
|
-
"@tanstack/react-query": "5.
|
213
|
+
"@tanstack/react-query": "5.56.2",
|
214
214
|
"@walletconnect/ethereum-provider": "2.16.1",
|
215
215
|
"@walletconnect/sign-client": "2.16.1",
|
216
216
|
"abitype": "1.0.5",
|
@@ -220,13 +220,13 @@
|
|
220
220
|
"mipd": "0.0.7",
|
221
221
|
"node-libs-browser": "2.2.1",
|
222
222
|
"uqr": "0.1.2",
|
223
|
-
"viem": "2.21.
|
223
|
+
"viem": "2.21.7"
|
224
224
|
},
|
225
225
|
"peerDependencies": {
|
226
226
|
"@aws-sdk/client-lambda": "^3",
|
227
227
|
"@aws-sdk/credential-providers": "^3",
|
228
228
|
"@coinbase/wallet-mobile-sdk": "^1",
|
229
|
-
"@mobile-wallet-protocol/client": "
|
229
|
+
"@mobile-wallet-protocol/client": "0.0.3",
|
230
230
|
"@react-native-async-storage/async-storage": "^1 || ^2",
|
231
231
|
"amazon-cognito-identity-js": "^6",
|
232
232
|
"aws-amplify": "^5",
|
@@ -297,28 +297,28 @@
|
|
297
297
|
"node": ">=18"
|
298
298
|
},
|
299
299
|
"devDependencies": {
|
300
|
-
"@aws-sdk/client-lambda": "3.
|
301
|
-
"@aws-sdk/credential-providers": "3.
|
302
|
-
"@chromatic-com/storybook": "
|
300
|
+
"@aws-sdk/client-lambda": "3.651.1",
|
301
|
+
"@aws-sdk/credential-providers": "3.651.1",
|
302
|
+
"@chromatic-com/storybook": "2.0.2",
|
303
303
|
"@codspeed/vitest-plugin": "3.1.1",
|
304
|
-
"@coinbase/wallet-mobile-sdk": "1.
|
304
|
+
"@coinbase/wallet-mobile-sdk": "1.1.2",
|
305
305
|
"@mobile-wallet-protocol/client": "0.0.2",
|
306
306
|
"@react-native-async-storage/async-storage": "1.24.0",
|
307
|
-
"@storybook/addon-essentials": "
|
308
|
-
"@storybook/addon-interactions": "
|
309
|
-
"@storybook/addon-links": "
|
310
|
-
"@storybook/addon-onboarding": "
|
311
|
-
"@storybook/blocks": "
|
312
|
-
"@storybook/react": "
|
313
|
-
"@storybook/react-vite": "
|
314
|
-
"@storybook/test": "
|
307
|
+
"@storybook/addon-essentials": "8.3.0",
|
308
|
+
"@storybook/addon-interactions": "8.3.0",
|
309
|
+
"@storybook/addon-links": "8.3.0",
|
310
|
+
"@storybook/addon-onboarding": "8.3.0",
|
311
|
+
"@storybook/blocks": "8.3.0",
|
312
|
+
"@storybook/react": "8.3.0",
|
313
|
+
"@storybook/react-vite": "8.3.0",
|
314
|
+
"@storybook/test": "8.3.0",
|
315
315
|
"@testing-library/jest-dom": "^6.4.7",
|
316
316
|
"@testing-library/react": "^16.0.0",
|
317
317
|
"@testing-library/user-event": "^14.5.2",
|
318
318
|
"@types/cross-spawn": "^6.0.6",
|
319
319
|
"@types/react": "^18.3.5",
|
320
320
|
"@vitejs/plugin-react": "^4.3.1",
|
321
|
-
"@vitest/ui": "2.
|
321
|
+
"@vitest/ui": "2.1.1",
|
322
322
|
"amazon-cognito-identity-js": "6.3.12",
|
323
323
|
"aws-amplify": "5.3.19",
|
324
324
|
"cross-spawn": "7.0.3",
|
@@ -327,15 +327,15 @@
|
|
327
327
|
"expo-linking": "6.3.1",
|
328
328
|
"expo-web-browser": "13.0.3",
|
329
329
|
"happy-dom": "^14.12.0",
|
330
|
-
"msw": "2.4.
|
331
|
-
"react-native": "0.75.
|
330
|
+
"msw": "2.4.7",
|
331
|
+
"react-native": "0.75.3",
|
332
332
|
"react-native-aes-gcm-crypto": "0.2.2",
|
333
333
|
"react-native-passkey": "3.0.0-beta2",
|
334
|
-
"react-native-quick-crypto": "0.7.
|
335
|
-
"react-native-svg": "15.
|
336
|
-
"storybook": "
|
334
|
+
"react-native-quick-crypto": "0.7.4",
|
335
|
+
"react-native-svg": "15.6.0",
|
336
|
+
"storybook": "8.3.0",
|
337
337
|
"typescript": "5.6.2",
|
338
|
-
"vite": "5.4.
|
338
|
+
"vite": "5.4.5"
|
339
339
|
},
|
340
340
|
"scripts": {
|
341
341
|
"bench:compare": "bun run ./benchmarks/run.ts",
|
@@ -12,7 +12,8 @@ import { withCache } from "../../utils/promise/withCache.js";
|
|
12
12
|
|
13
13
|
import { type ThirdwebContract, getContract } from "../contract.js";
|
14
14
|
|
15
|
-
const CONTRACT_PUBLISHER_ADDRESS =
|
15
|
+
export const CONTRACT_PUBLISHER_ADDRESS =
|
16
|
+
"0xf5b896Ddb5146D5dA77efF4efBb3Eae36E300808"; // Polygon only
|
16
17
|
export const THIRDWEB_DEPLOYER = "0xdd99b75f095d0c4d5112aCe938e4e6ed962fb024";
|
17
18
|
|
18
19
|
/**
|
@@ -35,10 +35,6 @@ export {
|
|
35
35
|
} from "../../extensions/thirdweb/__generated__/IContractPublisher/read/getPublishedContractVersions.js";
|
36
36
|
|
37
37
|
// Write
|
38
|
-
export {
|
39
|
-
publishContract,
|
40
|
-
type PublishContractParams,
|
41
|
-
} from "../../extensions/thirdweb/__generated__/IContractPublisher/write/publishContract.js";
|
42
38
|
export {
|
43
39
|
setPublisherProfileUri,
|
44
40
|
type SetPublisherProfileUriParams,
|
@@ -48,6 +44,12 @@ export {
|
|
48
44
|
type UnpublishContractParams,
|
49
45
|
} from "../../extensions/thirdweb/__generated__/IContractPublisher/write/unpublishContract.js";
|
50
46
|
|
47
|
+
export {
|
48
|
+
publishContract,
|
49
|
+
type PublishContractParams,
|
50
|
+
getContractPublisher,
|
51
|
+
} from "../../extensions/thirdweb/write/publish.js";
|
52
|
+
|
51
53
|
// --------------------------------------------------------
|
52
54
|
// Multichain Registry
|
53
55
|
// --------------------------------------------------------
|
package/src/exports/modules.ts
CHANGED
@@ -121,6 +121,7 @@ export {
|
|
121
121
|
rolesOf,
|
122
122
|
type RolesOfParams,
|
123
123
|
} from "../extensions/modules/__generated__/OwnableRoles/read/rolesOf.js";
|
124
|
+
export { checkModulesCompatibility } from "../extensions/modules/common/checkModulesCompatibility.js";
|
124
125
|
|
125
126
|
/**
|
126
127
|
* Write
|
@@ -158,7 +159,10 @@ export {
|
|
158
159
|
isUninstallModuleSupported,
|
159
160
|
type UninstallModuleParams,
|
160
161
|
} from "../extensions/modules/__generated__/IModularCore/write/uninstallModule.js";
|
161
|
-
export {
|
162
|
+
export {
|
163
|
+
getModuleConfig,
|
164
|
+
isGetModuleConfigSupported,
|
165
|
+
} from "../extensions/modules/__generated__/IModule/read/getModuleConfig.js";
|
162
166
|
export {
|
163
167
|
installPublishedModule,
|
164
168
|
type InstallPublishedModuleOptions,
|
@@ -21,7 +21,7 @@ import { getInstalledModules } from "../__generated__/IModularCore/read/getInsta
|
|
21
21
|
import { grantMinterRole } from "../common/grantMinterRole.js";
|
22
22
|
import * as MintableERC1155 from "./index.js";
|
23
23
|
|
24
|
-
describe("ModularTokenERC1155", () => {
|
24
|
+
describe.runIf(process.env.TW_SECRET_KEY)("ModularTokenERC1155", () => {
|
25
25
|
let contract: ThirdwebContract;
|
26
26
|
beforeAll(async () => {
|
27
27
|
const address = await deployModularContract({
|
@@ -16,7 +16,7 @@ import { getInstalledModules } from "../__generated__/IModularCore/read/getInsta
|
|
16
16
|
import { grantMinterRole } from "../common/grantMinterRole.js";
|
17
17
|
import * as MintableERC20 from "./index.js";
|
18
18
|
|
19
|
-
describe("ModularTokenERC20", () => {
|
19
|
+
describe.runIf(process.env.TW_SECRET_KEY)("ModularTokenERC20", () => {
|
20
20
|
let contract: ThirdwebContract;
|
21
21
|
beforeAll(async () => {
|
22
22
|
const address = await deployModularContract({
|
@@ -20,7 +20,7 @@ import { getInstalledModules } from "../__generated__/IModularCore/read/getInsta
|
|
20
20
|
import { grantMinterRole } from "../common/grantMinterRole.js";
|
21
21
|
import * as MintableERC721 from "./index.js";
|
22
22
|
|
23
|
-
describe("ModularTokenERC721", () => {
|
23
|
+
describe.runIf(process.env.TW_SECRET_KEY)("ModularTokenERC721", () => {
|
24
24
|
let contract: ThirdwebContract;
|
25
25
|
beforeAll(async () => {
|
26
26
|
const address = await deployModularContract({
|
@@ -0,0 +1,46 @@
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
2
|
+
import { CLAIMABLE_ERC20_MODULE_BYTECODE } from "../../../../test/src/bytecode/claimable-erc20.js";
|
3
|
+
import { CLAIMABLE_ERC721_BYTECODE } from "../../../../test/src/bytecode/claimable-erc721.js";
|
4
|
+
import { ERC20_CORE_BYTECODE } from "../../../../test/src/bytecode/erc20core.js";
|
5
|
+
import { MINTABLE_ERC20_BYTECODE } from "../../../../test/src/bytecode/mintable-erc20.js";
|
6
|
+
import { ANVIL_CHAIN } from "../../../../test/src/chains.js";
|
7
|
+
import { TEST_CLIENT } from "../../../../test/src/test-clients.js";
|
8
|
+
import { checkModulesCompatibility } from "./checkModulesCompatibility.js";
|
9
|
+
|
10
|
+
describe("compatibleModules", () => {
|
11
|
+
it("should return true for compatible modules", async () => {
|
12
|
+
const result = await checkModulesCompatibility({
|
13
|
+
coreBytecode: ERC20_CORE_BYTECODE,
|
14
|
+
moduleBytecodes: [CLAIMABLE_ERC20_MODULE_BYTECODE],
|
15
|
+
chain: ANVIL_CHAIN,
|
16
|
+
client: TEST_CLIENT,
|
17
|
+
});
|
18
|
+
|
19
|
+
expect(result).toBe(true);
|
20
|
+
});
|
21
|
+
|
22
|
+
it("should return false for incompatible modules", async () => {
|
23
|
+
const result = await checkModulesCompatibility({
|
24
|
+
coreBytecode: ERC20_CORE_BYTECODE,
|
25
|
+
moduleBytecodes: [CLAIMABLE_ERC721_BYTECODE],
|
26
|
+
chain: ANVIL_CHAIN,
|
27
|
+
client: TEST_CLIENT,
|
28
|
+
});
|
29
|
+
|
30
|
+
expect(result).toBe(false);
|
31
|
+
});
|
32
|
+
|
33
|
+
it("should return false for overlapping modules", async () => {
|
34
|
+
const result = await checkModulesCompatibility({
|
35
|
+
coreBytecode: ERC20_CORE_BYTECODE,
|
36
|
+
moduleBytecodes: [
|
37
|
+
CLAIMABLE_ERC20_MODULE_BYTECODE,
|
38
|
+
MINTABLE_ERC20_BYTECODE,
|
39
|
+
],
|
40
|
+
chain: ANVIL_CHAIN,
|
41
|
+
client: TEST_CLIENT,
|
42
|
+
});
|
43
|
+
|
44
|
+
expect(result).toBe(false);
|
45
|
+
});
|
46
|
+
});
|
@@ -0,0 +1,123 @@
|
|
1
|
+
import {} from "abitype";
|
2
|
+
import type { Chain } from "../../../chains/types.js";
|
3
|
+
import type { ThirdwebClient } from "../../../client/client.js";
|
4
|
+
import { eth_call } from "../../../rpc/actions/eth_call.js";
|
5
|
+
import { getRpcClient } from "../../../rpc/rpc.js";
|
6
|
+
import { hasDuplicates } from "../../../utils/arrays.js";
|
7
|
+
import { ensureBytecodePrefix } from "../../../utils/bytecode/prefix.js";
|
8
|
+
import type { Hex } from "../../../utils/encoding/hex.js";
|
9
|
+
import {
|
10
|
+
decodeSupportsInterfaceResult,
|
11
|
+
encodeSupportsInterface,
|
12
|
+
} from "../../erc165/__generated__/IERC165/read/supportsInterface.js";
|
13
|
+
import {
|
14
|
+
decodeGetSupportedCallbackFunctionsResult,
|
15
|
+
FN_SELECTOR as getSupportedCallbackFunctionsSelector,
|
16
|
+
} from "../__generated__/IModularCore/read/getSupportedCallbackFunctions.js";
|
17
|
+
import {
|
18
|
+
decodeGetModuleConfigResult,
|
19
|
+
FN_SELECTOR as getModuleConfigSelector,
|
20
|
+
} from "../__generated__/IModule/read/getModuleConfig.js";
|
21
|
+
|
22
|
+
export async function checkModulesCompatibility(options: {
|
23
|
+
coreBytecode: string;
|
24
|
+
moduleBytecodes: string[];
|
25
|
+
chain: Chain;
|
26
|
+
client: ThirdwebClient;
|
27
|
+
}): Promise<boolean> {
|
28
|
+
const addr = "0x0000000000000000000000000000000000000124"; // arbitrary address
|
29
|
+
let _coreBytecode = ensureBytecodePrefix(options.coreBytecode);
|
30
|
+
if (!_coreBytecode.startsWith("0x6080604052")) {
|
31
|
+
const index = _coreBytecode.indexOf("6080604052");
|
32
|
+
_coreBytecode = `0x${_coreBytecode.substring(index)}`;
|
33
|
+
} else if (_coreBytecode.lastIndexOf("6080604052") > 0) {
|
34
|
+
const index = _coreBytecode.lastIndexOf("6080604052");
|
35
|
+
_coreBytecode = `0x${_coreBytecode.substring(index)}`;
|
36
|
+
}
|
37
|
+
const rpcClient = getRpcClient({
|
38
|
+
client: options.client,
|
39
|
+
chain: options.chain,
|
40
|
+
});
|
41
|
+
|
42
|
+
// get the core's supported callback functions
|
43
|
+
const coreCallResult = await eth_call(rpcClient, {
|
44
|
+
data: getSupportedCallbackFunctionsSelector,
|
45
|
+
to: addr,
|
46
|
+
stateOverrides: {
|
47
|
+
[addr]: {
|
48
|
+
code: _coreBytecode,
|
49
|
+
},
|
50
|
+
},
|
51
|
+
});
|
52
|
+
|
53
|
+
const decodedCallResult =
|
54
|
+
decodeGetSupportedCallbackFunctionsResult(coreCallResult);
|
55
|
+
const coreCallbackSelectors = decodedCallResult.flat().map((c) => c.selector);
|
56
|
+
|
57
|
+
// get the module config for each module
|
58
|
+
const modules = await Promise.all(
|
59
|
+
options.moduleBytecodes.map(async (b: string) => {
|
60
|
+
// TODO: Upload deployed bytecode on publish metadata
|
61
|
+
let moduleBytecode = ensureBytecodePrefix(b);
|
62
|
+
if (!moduleBytecode.startsWith("0x6080604052")) {
|
63
|
+
const index = moduleBytecode.indexOf("6080604052");
|
64
|
+
moduleBytecode = `0x${moduleBytecode.substring(index)}`;
|
65
|
+
} else if (moduleBytecode.lastIndexOf("6080604052") > 0) {
|
66
|
+
const index = moduleBytecode.lastIndexOf("6080604052");
|
67
|
+
moduleBytecode = `0x${moduleBytecode.substring(index)}`;
|
68
|
+
}
|
69
|
+
|
70
|
+
const callResult = await eth_call(rpcClient, {
|
71
|
+
data: getModuleConfigSelector,
|
72
|
+
to: addr,
|
73
|
+
stateOverrides: {
|
74
|
+
[addr]: {
|
75
|
+
code: moduleBytecode,
|
76
|
+
},
|
77
|
+
},
|
78
|
+
});
|
79
|
+
return decodeGetModuleConfigResult(callResult);
|
80
|
+
}),
|
81
|
+
);
|
82
|
+
|
83
|
+
// check if callback selectors are supported
|
84
|
+
for (const module of modules) {
|
85
|
+
for (const callback of module.callbackFunctions) {
|
86
|
+
if (!coreCallbackSelectors.includes(callback.selector)) {
|
87
|
+
return false;
|
88
|
+
}
|
89
|
+
}
|
90
|
+
}
|
91
|
+
|
92
|
+
// check if the core contract supports required interfaces by modules above
|
93
|
+
const requiredInterfaces = modules.flatMap((m) => m.requiredInterfaces);
|
94
|
+
if (requiredInterfaces.length > 0) {
|
95
|
+
const supportsInterfaceResult = await Promise.all(
|
96
|
+
requiredInterfaces.map(async (r) => {
|
97
|
+
const callResult = await eth_call(rpcClient, {
|
98
|
+
data: encodeSupportsInterface({
|
99
|
+
interfaceId: r,
|
100
|
+
}),
|
101
|
+
to: addr,
|
102
|
+
stateOverrides: {
|
103
|
+
[addr]: {
|
104
|
+
code: _coreBytecode,
|
105
|
+
},
|
106
|
+
},
|
107
|
+
});
|
108
|
+
return decodeSupportsInterfaceResult(callResult);
|
109
|
+
}),
|
110
|
+
);
|
111
|
+
|
112
|
+
if (supportsInterfaceResult.flat().some((element) => element === false)) {
|
113
|
+
return false;
|
114
|
+
}
|
115
|
+
}
|
116
|
+
return !hasDuplicates(
|
117
|
+
[
|
118
|
+
...modules.flatMap((m) => m.callbackFunctions.map((c) => c.selector)),
|
119
|
+
...modules.flatMap((m) => m.fallbackFunctions.map((f) => f.selector)),
|
120
|
+
],
|
121
|
+
(a: Hex | undefined, b: Hex | undefined): boolean => a === b,
|
122
|
+
);
|
123
|
+
}
|
@@ -0,0 +1,162 @@
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
2
|
+
import { FORKED_POLYGON_CHAIN } from "../../../../test/src/chains.js";
|
3
|
+
import { TEST_CLIENT } from "../../../../test/src/test-clients.js";
|
4
|
+
import { TEST_ACCOUNT_D } from "../../../../test/src/test-wallets.js";
|
5
|
+
import { getContract } from "../../../contract/contract.js";
|
6
|
+
import { CONTRACT_PUBLISHER_ADDRESS } from "../../../contract/deployment/publisher.js";
|
7
|
+
import { parseEventLogs } from "../../../event/actions/parse-logs.js";
|
8
|
+
import { download } from "../../../storage/download.js";
|
9
|
+
import { sendAndConfirmTransaction } from "../../../transaction/actions/send-and-confirm-transaction.js";
|
10
|
+
import { fetchDeployMetadata } from "../../../utils/any-evm/deploy-metadata.js";
|
11
|
+
import { contractPublishedEvent } from "../__generated__/IContractPublisher/events/ContractPublished.js";
|
12
|
+
import { getAllPublishedContracts } from "../__generated__/IContractPublisher/read/getAllPublishedContracts.js";
|
13
|
+
import { getPublishedContractVersions } from "../__generated__/IContractPublisher/read/getPublishedContractVersions.js";
|
14
|
+
import { publishContract } from "./publish.js";
|
15
|
+
|
16
|
+
describe.runIf(process.env.TW_SECRET_KEY)("publishContract", () => {
|
17
|
+
it("should publish a contract successfully", async () => {
|
18
|
+
const publisherContract = getContract({
|
19
|
+
client: TEST_CLIENT,
|
20
|
+
chain: FORKED_POLYGON_CHAIN,
|
21
|
+
address: CONTRACT_PUBLISHER_ADDRESS,
|
22
|
+
});
|
23
|
+
|
24
|
+
let publishedContracts = await getAllPublishedContracts({
|
25
|
+
contract: publisherContract,
|
26
|
+
publisher: TEST_ACCOUNT_D.address,
|
27
|
+
});
|
28
|
+
|
29
|
+
expect(publishedContracts.length).toBe(0);
|
30
|
+
|
31
|
+
const catAttackDeployMetadata = await fetchDeployMetadata({
|
32
|
+
client: TEST_CLIENT,
|
33
|
+
uri: "ipfs://QmWcAMvBy49WRrzZeK4EQeVnkdmyb5H4STz4gUQwnt1kzC",
|
34
|
+
});
|
35
|
+
|
36
|
+
const tx = publishContract({
|
37
|
+
contract: publisherContract,
|
38
|
+
account: TEST_ACCOUNT_D,
|
39
|
+
metadata: {
|
40
|
+
...catAttackDeployMetadata,
|
41
|
+
version: "0.0.1",
|
42
|
+
description: "Cat Attack NFT",
|
43
|
+
changelog: "Initial release",
|
44
|
+
},
|
45
|
+
});
|
46
|
+
const result = await sendAndConfirmTransaction({
|
47
|
+
transaction: tx,
|
48
|
+
account: TEST_ACCOUNT_D,
|
49
|
+
});
|
50
|
+
expect(result.transactionHash.length).toBeGreaterThan(0);
|
51
|
+
const logs = parseEventLogs({
|
52
|
+
events: [contractPublishedEvent()],
|
53
|
+
logs: result.logs,
|
54
|
+
});
|
55
|
+
expect(logs?.[0]?.args.publishedContract.contractId).toBe("CatAttackNFT");
|
56
|
+
expect(logs?.[0]?.args.publishedContract.publishMetadataUri).toBeDefined();
|
57
|
+
const rawMeta = await download({
|
58
|
+
client: TEST_CLIENT,
|
59
|
+
uri: logs?.[0]?.args.publishedContract.publishMetadataUri ?? "",
|
60
|
+
}).then((r) => r.json());
|
61
|
+
expect(rawMeta).toMatchInlineSnapshot(`
|
62
|
+
{
|
63
|
+
"bytecodeUri": "ipfs://QmVyB9qAs7XdZYNGPcNbff43BX1tyZFJkqdfp1eXiNS8AG/0",
|
64
|
+
"changelog": "Initial release",
|
65
|
+
"compilers": {
|
66
|
+
"solc": [
|
67
|
+
{
|
68
|
+
"bytecodeUri": "ipfs://QmVyB9qAs7XdZYNGPcNbff43BX1tyZFJkqdfp1eXiNS8AG/0",
|
69
|
+
"compilerVersion": "",
|
70
|
+
"evmVersion": "",
|
71
|
+
"metadataUri": "ipfs://Qmd2Ef29NzCjomqYXZbWa8ZdF1AESDAS1HDAonmAnTgHPs",
|
72
|
+
},
|
73
|
+
],
|
74
|
+
},
|
75
|
+
"description": "Cat Attack NFT",
|
76
|
+
"metadataUri": "ipfs://Qmd2Ef29NzCjomqYXZbWa8ZdF1AESDAS1HDAonmAnTgHPs",
|
77
|
+
"name": "CatAttackNFT",
|
78
|
+
"publisher": "0x90F79bf6EB2c4f870365E785982E1f101E93b906",
|
79
|
+
"routerType": "none",
|
80
|
+
"version": "0.0.1",
|
81
|
+
}
|
82
|
+
`);
|
83
|
+
const publishedData = await fetchDeployMetadata({
|
84
|
+
client: TEST_CLIENT,
|
85
|
+
uri: logs?.[0]?.args.publishedContract.publishMetadataUri ?? "",
|
86
|
+
});
|
87
|
+
expect(publishedData.abi).toBeDefined();
|
88
|
+
expect(publishedData.bytecode).toBeDefined();
|
89
|
+
expect(publishedData.version).toBe("0.0.1");
|
90
|
+
expect(publishedData.changelog).toBe("Initial release");
|
91
|
+
expect(publishedData.name).toBe("CatAttackNFT");
|
92
|
+
expect(publishedData.description).toBe("Cat Attack NFT");
|
93
|
+
expect(publishedData.publisher).toBe(TEST_ACCOUNT_D.address);
|
94
|
+
expect(publishedData.routerType).toBe("none");
|
95
|
+
|
96
|
+
publishedContracts = await getAllPublishedContracts({
|
97
|
+
contract: publisherContract,
|
98
|
+
publisher: TEST_ACCOUNT_D.address,
|
99
|
+
});
|
100
|
+
|
101
|
+
expect(publishedContracts.length).toBe(1);
|
102
|
+
|
103
|
+
expect(
|
104
|
+
sendAndConfirmTransaction({
|
105
|
+
account: TEST_ACCOUNT_D,
|
106
|
+
transaction: publishContract({
|
107
|
+
contract: publisherContract,
|
108
|
+
account: TEST_ACCOUNT_D,
|
109
|
+
previousMetadata: publishedData,
|
110
|
+
metadata: {
|
111
|
+
...publishedData,
|
112
|
+
version: "0.0.1",
|
113
|
+
changelog: "Initial release 2",
|
114
|
+
},
|
115
|
+
}),
|
116
|
+
}),
|
117
|
+
).rejects.toThrow("Version 0.0.1 is not greater than 0.0.1");
|
118
|
+
|
119
|
+
const tx2 = publishContract({
|
120
|
+
contract: publisherContract,
|
121
|
+
account: TEST_ACCOUNT_D,
|
122
|
+
previousMetadata: publishedData,
|
123
|
+
metadata: {
|
124
|
+
...publishedData,
|
125
|
+
version: "0.0.2",
|
126
|
+
changelog: "Initial release 2",
|
127
|
+
},
|
128
|
+
});
|
129
|
+
const result2 = await sendAndConfirmTransaction({
|
130
|
+
transaction: tx2,
|
131
|
+
account: TEST_ACCOUNT_D,
|
132
|
+
});
|
133
|
+
|
134
|
+
expect(result2.transactionHash.length).toBeGreaterThan(0);
|
135
|
+
const logs2 = parseEventLogs({
|
136
|
+
events: [contractPublishedEvent()],
|
137
|
+
logs: result2.logs,
|
138
|
+
});
|
139
|
+
expect(logs2?.[0]?.args.publishedContract.contractId).toBe("CatAttackNFT");
|
140
|
+
expect(logs2?.[0]?.args.publishedContract.publishMetadataUri).toBeDefined();
|
141
|
+
const publishedData2 = await fetchDeployMetadata({
|
142
|
+
client: TEST_CLIENT,
|
143
|
+
uri: logs2?.[0]?.args.publishedContract.publishMetadataUri ?? "",
|
144
|
+
});
|
145
|
+
expect(publishedData2.version).toBe("0.0.2");
|
146
|
+
|
147
|
+
publishedContracts = await getAllPublishedContracts({
|
148
|
+
contract: publisherContract,
|
149
|
+
publisher: TEST_ACCOUNT_D.address,
|
150
|
+
});
|
151
|
+
|
152
|
+
expect(publishedContracts.length).toBe(1);
|
153
|
+
|
154
|
+
const versions = await getPublishedContractVersions({
|
155
|
+
contract: publisherContract,
|
156
|
+
contractId: "CatAttackNFT",
|
157
|
+
publisher: TEST_ACCOUNT_D.address,
|
158
|
+
});
|
159
|
+
|
160
|
+
expect(versions.length).toEqual(2);
|
161
|
+
}, 120000);
|
162
|
+
});
|
@@ -0,0 +1,131 @@
|
|
1
|
+
import type { Abi } from "abitype";
|
2
|
+
import { encodePacked, keccak256, toFunctionSelector } from "viem/utils";
|
3
|
+
import { polygon } from "../../../chains/chain-definitions/polygon.js";
|
4
|
+
import type { ThirdwebClient } from "../../../client/client.js";
|
5
|
+
import { ZERO_ADDRESS } from "../../../constants/addresses.js";
|
6
|
+
import { getContract } from "../../../contract/contract.js";
|
7
|
+
import { CONTRACT_PUBLISHER_ADDRESS } from "../../../contract/deployment/publisher.js";
|
8
|
+
import { download } from "../../../storage/download.js";
|
9
|
+
import { upload } from "../../../storage/upload.js";
|
10
|
+
import type { BaseTransactionOptions } from "../../../transaction/types.js";
|
11
|
+
import type {
|
12
|
+
ExtendedMetadata,
|
13
|
+
FetchDeployMetadataResult,
|
14
|
+
} from "../../../utils/any-evm/deploy-metadata.js";
|
15
|
+
import { ensureBytecodePrefix } from "../../../utils/bytecode/prefix.js";
|
16
|
+
import { isIncrementalVersion } from "../../../utils/semver.js";
|
17
|
+
import type { Account } from "../../../wallets/interfaces/wallet.js";
|
18
|
+
import { isGetInstalledModulesSupported } from "../../modules/__generated__/IModularCore/read/getInstalledModules.js";
|
19
|
+
import { publishContract as generatedPublishContract } from "../__generated__/IContractPublisher/write/publishContract.js";
|
20
|
+
|
21
|
+
export type PublishContractParams = {
|
22
|
+
account: Account;
|
23
|
+
metadata: FetchDeployMetadataResult & {
|
24
|
+
version: string;
|
25
|
+
};
|
26
|
+
previousMetadata?: FetchDeployMetadataResult;
|
27
|
+
};
|
28
|
+
|
29
|
+
/**
|
30
|
+
* Publish a contract to the contract publisher.
|
31
|
+
*
|
32
|
+
* @param options - The options for publishing the contract.
|
33
|
+
* @returns The transaction to publish the contract.
|
34
|
+
* @example
|
35
|
+
* ```ts
|
36
|
+
* const tx = publishContract({
|
37
|
+
* contract,
|
38
|
+
* account,
|
39
|
+
* metadata,
|
40
|
+
* });
|
41
|
+
* ```
|
42
|
+
* @extension thirdweb
|
43
|
+
*/
|
44
|
+
export function publishContract(
|
45
|
+
options: BaseTransactionOptions<PublishContractParams>,
|
46
|
+
) {
|
47
|
+
return generatedPublishContract({
|
48
|
+
contract: options.contract,
|
49
|
+
async asyncParams() {
|
50
|
+
const currentVersion = options.previousMetadata?.version;
|
51
|
+
// check if the version is greater than the current version
|
52
|
+
if (
|
53
|
+
currentVersion &&
|
54
|
+
!isIncrementalVersion(currentVersion, options.metadata.version)
|
55
|
+
) {
|
56
|
+
throw Error(
|
57
|
+
`Version ${options.metadata.version} is not greater than ${currentVersion}`,
|
58
|
+
);
|
59
|
+
}
|
60
|
+
// hash the bytecode
|
61
|
+
const bytecode = await download({
|
62
|
+
client: options.contract.client,
|
63
|
+
uri: options.metadata.bytecodeUri,
|
64
|
+
}).then((r) => r.text());
|
65
|
+
const bytecodeHash = keccak256(
|
66
|
+
encodePacked(["bytes"], [ensureBytecodePrefix(bytecode)]),
|
67
|
+
);
|
68
|
+
|
69
|
+
const abi = options.metadata.abi;
|
70
|
+
const routerType = getRouterType(abi);
|
71
|
+
// not spreading here, we don't want to re-upload the fetched data like bytecode
|
72
|
+
const newMetadata: ExtendedMetadata = {
|
73
|
+
bytecodeUri: options.metadata.bytecodeUri,
|
74
|
+
metadataUri: options.metadata.metadataUri,
|
75
|
+
name: options.metadata.name,
|
76
|
+
version: options.metadata.version,
|
77
|
+
audit: options.metadata.audit,
|
78
|
+
changelog: options.metadata.changelog,
|
79
|
+
compositeAbi: options.metadata.compositeAbi,
|
80
|
+
constructorParams: options.metadata.constructorParams,
|
81
|
+
defaultExtensions: options.metadata.defaultExtensions,
|
82
|
+
defaultModules: options.metadata.defaultModules,
|
83
|
+
deployType: options.metadata.deployType,
|
84
|
+
description: options.metadata.description,
|
85
|
+
displayName: options.metadata.displayName,
|
86
|
+
factoryDeploymentData: options.metadata.factoryDeploymentData,
|
87
|
+
isDeployableViaFactory: options.metadata.isDeployableViaFactory,
|
88
|
+
isDeployableViaProxy: options.metadata.isDeployableViaProxy,
|
89
|
+
logo: options.metadata.logo,
|
90
|
+
networksForDeployment: options.metadata.networksForDeployment,
|
91
|
+
readme: options.metadata.readme,
|
92
|
+
tags: options.metadata.tags,
|
93
|
+
compilers: options.metadata.compilers,
|
94
|
+
publisher: options.account.address,
|
95
|
+
routerType,
|
96
|
+
};
|
97
|
+
|
98
|
+
// upload the new metadata
|
99
|
+
const newMetadataUri = await upload({
|
100
|
+
client: options.contract.client,
|
101
|
+
files: [newMetadata],
|
102
|
+
});
|
103
|
+
|
104
|
+
return {
|
105
|
+
publisher: options.account.address,
|
106
|
+
contractId: options.metadata.name,
|
107
|
+
publishMetadataUri: newMetadataUri,
|
108
|
+
compilerMetadataUri: options.metadata.metadataUri,
|
109
|
+
bytecodeHash,
|
110
|
+
implementation: ZERO_ADDRESS,
|
111
|
+
};
|
112
|
+
},
|
113
|
+
});
|
114
|
+
}
|
115
|
+
|
116
|
+
export function getContractPublisher(client: ThirdwebClient) {
|
117
|
+
return getContract({
|
118
|
+
client,
|
119
|
+
chain: polygon,
|
120
|
+
address: CONTRACT_PUBLISHER_ADDRESS,
|
121
|
+
});
|
122
|
+
}
|
123
|
+
|
124
|
+
function getRouterType(abi: Abi) {
|
125
|
+
const fnSelectors = abi
|
126
|
+
.filter((f) => f.type === "function")
|
127
|
+
.map((f) => toFunctionSelector(f));
|
128
|
+
const isModule = isGetInstalledModulesSupported(fnSelectors);
|
129
|
+
// TODO add dynamic detection
|
130
|
+
return isModule ? "modular" : "none";
|
131
|
+
}
|