Skip to content

Commit 046ac96

Browse files
authored
Merge branch 'main' into saminacodes-patch-1
2 parents f17f47e + 1273cc7 commit 046ac96

File tree

5 files changed

+266
-14
lines changed

5 files changed

+266
-14
lines changed

.changeset/fresh-gorillas-sip.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"thirdweb": patch
3+
---
4+
5+
Compute addresses with ref contracts
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
import type { Chain } from "../../chains/types.js";
2+
import type { ThirdwebClient } from "../../client/client.js";
3+
import { encodeAbiParameters } from "../../utils/abi/encodeAbiParameters.js";
4+
import { computePublishedContractAddress } from "../../utils/any-evm/compute-published-contract-address.js";
5+
import type { ImplementationConstructorParam } from "./process-ref-deployments.js";
6+
7+
type ComputeRefDeploymentsOptions = {
8+
client: ThirdwebClient;
9+
chain: Chain;
10+
paramValue: string | ImplementationConstructorParam;
11+
};
12+
13+
/**
14+
* Computes addresses for published contract references in constructor params.
15+
* @returns Param value after processing references.
16+
* @internal
17+
*/
18+
export async function computeRefDeployments(
19+
options: ComputeRefDeploymentsOptions,
20+
): Promise<string | string[]> {
21+
const { client, chain, paramValue } = options;
22+
23+
if (typeof paramValue === "object") {
24+
if (
25+
"defaultValue" in paramValue &&
26+
paramValue.defaultValue &&
27+
paramValue.defaultValue.length > 0
28+
) {
29+
return paramValue.defaultValue;
30+
}
31+
32+
if ("dynamicValue" in paramValue && paramValue.dynamicValue) {
33+
const dynamicValue = paramValue.dynamicValue;
34+
const contracts = dynamicValue.refContracts;
35+
36+
if (dynamicValue.type === "address") {
37+
if (!contracts || contracts.length === 0 || !contracts[0]?.contractId) {
38+
throw new Error("Invalid or empty param value");
39+
}
40+
const salt =
41+
contracts[0]?.salt && contracts[0]?.salt.length > 0
42+
? contracts[0]?.salt
43+
: "";
44+
45+
const addr = await computePublishedContractAddress({
46+
client,
47+
chain,
48+
contractId: contracts[0]?.contractId,
49+
publisher: contracts[0]?.publisherAddress,
50+
version: contracts[0]?.version,
51+
salt,
52+
});
53+
54+
return addr;
55+
}
56+
57+
if (dynamicValue.type === "address[]") {
58+
if (!contracts || contracts.length === 0) {
59+
throw new Error("Invalid or empty param value");
60+
}
61+
const addressArray: string[] = [];
62+
63+
for (const c of contracts) {
64+
const salt = c?.salt && c?.salt.length > 0 ? c?.salt : "";
65+
66+
addressArray.push(
67+
await computePublishedContractAddress({
68+
client,
69+
chain,
70+
contractId: c.contractId,
71+
publisher: c.publisherAddress,
72+
version: c.version,
73+
salt,
74+
}),
75+
);
76+
}
77+
78+
return addressArray;
79+
}
80+
81+
if (dynamicValue.type === "bytes") {
82+
if (!dynamicValue.paramsToEncode) {
83+
throw new Error("Invalid or empty param value");
84+
}
85+
const paramsToEncode = dynamicValue.paramsToEncode[0];
86+
87+
if (paramsToEncode) {
88+
const types: string[] = [];
89+
const values: (string | string[])[] = [];
90+
for (const v of paramsToEncode) {
91+
types.push(v.type);
92+
93+
if (v.defaultValue) {
94+
values.push(v.defaultValue);
95+
} else if (v.dynamicValue) {
96+
values.push(
97+
await computeRefDeployments({
98+
client,
99+
chain,
100+
paramValue: v,
101+
}),
102+
);
103+
}
104+
}
105+
106+
return encodeAbiParameters(
107+
types.map((t) => {
108+
return { type: t };
109+
}),
110+
values,
111+
);
112+
}
113+
}
114+
115+
if (dynamicValue.type === "bytes[]") {
116+
if (!dynamicValue.paramsToEncode) {
117+
throw new Error("Invalid or empty param value");
118+
}
119+
const bytesArray: string[] = [];
120+
const paramArray = dynamicValue.paramsToEncode;
121+
122+
for (const a of paramArray) {
123+
const paramsToEncode = a;
124+
125+
if (paramsToEncode) {
126+
const types: string[] = [];
127+
const values: (string | string[])[] = [];
128+
for (const v of paramsToEncode) {
129+
types.push(v.type);
130+
131+
if (v.defaultValue) {
132+
values.push(v.defaultValue);
133+
} else if (v.dynamicValue) {
134+
values.push(
135+
await computeRefDeployments({
136+
client,
137+
chain,
138+
paramValue: v,
139+
}),
140+
);
141+
}
142+
}
143+
144+
bytesArray.push(
145+
encodeAbiParameters(
146+
types.map((t) => {
147+
return { type: t };
148+
}),
149+
values,
150+
),
151+
);
152+
}
153+
}
154+
155+
return bytesArray;
156+
}
157+
}
158+
}
159+
160+
return paramValue as string;
161+
}

packages/thirdweb/src/extensions/prebuilts/process-ref-deployments.test.ts

Lines changed: 73 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { ANVIL_CHAIN } from "../../../test/src/chains.js";
33
import { TEST_CLIENT } from "../../../test/src/test-clients.js";
44
import { TEST_ACCOUNT_A } from "../../../test/src/test-wallets.js";
55
import { getContract } from "../../contract/contract.js";
6+
import { getDeployedInfraContract } from "../../contract/deployment/utils/infra.js";
67
import { readContract } from "../../transaction/read-contract.js";
78
import { getInstalledModules } from "../modules/__generated__/IModularCore/read/getInstalledModules.js";
89
import { deployPublishedContract } from "./deploy-published.js";
@@ -29,16 +30,16 @@ describe.runIf(process.env.TW_SECRET_KEY)(
2930
account: TEST_ACCOUNT_A,
3031
contractId: "MultiSig",
3132
version: "0.0.4",
32-
salt: "tw",
33+
salt: "",
3334
publisher: "0x6453a486d52e0EB6E79Ec4491038E2522a926936",
3435
});
3536
dummyContractAddress = await deployPublishedContract({
3637
client: TEST_CLIENT,
3738
chain: ANVIL_CHAIN,
3839
account: TEST_ACCOUNT_A,
3940
contractId: "ContractWithBytes",
40-
version: "0.0.1",
41-
salt: "tw",
41+
version: "0.0.2",
42+
salt: "",
4243
publisher: "0x6453a486d52e0EB6E79Ec4491038E2522a926936",
4344
});
4445
mintfeeManagerModuleAddress = await deployPublishedContract({
@@ -47,35 +48,34 @@ describe.runIf(process.env.TW_SECRET_KEY)(
4748
account: TEST_ACCOUNT_A,
4849
contractId: "MintFeeManagerModule",
4950
version: "0.0.1",
50-
salt: "tw",
51+
salt: "",
5152
publisher: "0x6453a486d52e0EB6E79Ec4491038E2522a926936",
5253
});
5354
mintfeeManagerCoreAddress = await deployPublishedContract({
5455
client: TEST_CLIENT,
5556
chain: ANVIL_CHAIN,
5657
account: TEST_ACCOUNT_A,
5758
contractId: "MintFeeManagerCore",
58-
version: "0.0.25",
59-
salt: "tw",
59+
version: "0.0.26",
60+
salt: "",
6061
publisher: "0x6453a486d52e0EB6E79Ec4491038E2522a926936",
6162
});
6263
claimableModuleAddress = await deployPublishedContract({
6364
client: TEST_CLIENT,
6465
chain: ANVIL_CHAIN,
6566
account: TEST_ACCOUNT_A,
6667
contractId: "ClaimableERC721",
67-
version: "0.0.13",
68-
salt: "tw",
68+
version: "0.0.14",
69+
salt: "",
6970
publisher: "0x6453a486d52e0EB6E79Ec4491038E2522a926936",
7071
});
71-
7272
wethAddress = await deployPublishedContract({
7373
client: TEST_CLIENT,
7474
chain: ANVIL_CHAIN,
7575
account: TEST_ACCOUNT_A,
7676
contractId: "WETH9",
7777
version: "0.0.1",
78-
salt: "thirdweb",
78+
salt: "",
7979
publisher: "0x6453a486d52e0EB6E79Ec4491038E2522a926936",
8080
});
8181
forwarderAddress = await deployPublishedContract({
@@ -84,7 +84,7 @@ describe.runIf(process.env.TW_SECRET_KEY)(
8484
account: TEST_ACCOUNT_A,
8585
contractId: "Forwarder",
8686
version: "0.0.1",
87-
salt: "thirdweb",
87+
salt: "",
8888
publisher: "0x6453a486d52e0EB6E79Ec4491038E2522a926936",
8989
});
9090
multiwrapAddress = await deployPublishedContract({
@@ -192,5 +192,67 @@ describe.runIf(process.env.TW_SECRET_KEY)(
192192
expect(fetchedWethAddress.toLowerCase()).to.eq(wethAddress);
193193
expect(isTrustedForwarder).to.be.true;
194194
});
195+
196+
it("should correctly compute addresses for deployed contracts with refs", async () => {
197+
const [
198+
multisigAddressComputed,
199+
dummyContractAddressComputed,
200+
mintfeeManagerModuleAddressComputed,
201+
mintfeeManagerCoreAddressComputed,
202+
claimableModuleAddressComputed,
203+
] = await Promise.all([
204+
getDeployedInfraContract({
205+
client: TEST_CLIENT,
206+
chain: ANVIL_CHAIN,
207+
contractId: "MultiSig",
208+
version: "0.0.4",
209+
publisher: "0x6453a486d52e0EB6E79Ec4491038E2522a926936",
210+
}),
211+
getDeployedInfraContract({
212+
client: TEST_CLIENT,
213+
chain: ANVIL_CHAIN,
214+
contractId: "ContractWithBytes",
215+
version: "0.0.2",
216+
publisher: "0x6453a486d52e0EB6E79Ec4491038E2522a926936",
217+
}),
218+
getDeployedInfraContract({
219+
client: TEST_CLIENT,
220+
chain: ANVIL_CHAIN,
221+
contractId: "MintFeeManagerModule",
222+
version: "0.0.1",
223+
publisher: "0x6453a486d52e0EB6E79Ec4491038E2522a926936",
224+
}),
225+
getDeployedInfraContract({
226+
client: TEST_CLIENT,
227+
chain: ANVIL_CHAIN,
228+
contractId: "MintFeeManagerCore",
229+
version: "0.0.26",
230+
publisher: "0x6453a486d52e0EB6E79Ec4491038E2522a926936",
231+
}),
232+
getDeployedInfraContract({
233+
client: TEST_CLIENT,
234+
chain: ANVIL_CHAIN,
235+
contractId: "ClaimableERC721",
236+
version: "0.0.14",
237+
publisher: "0x6453a486d52e0EB6E79Ec4491038E2522a926936",
238+
}),
239+
]);
240+
241+
expect(multisigAddressComputed?.address.toLowerCase()).to.eq(
242+
multisigAddress,
243+
);
244+
expect(dummyContractAddressComputed?.address.toLowerCase()).to.eq(
245+
dummyContractAddress,
246+
);
247+
expect(mintfeeManagerModuleAddressComputed?.address.toLowerCase()).to.eq(
248+
mintfeeManagerModuleAddress,
249+
);
250+
expect(mintfeeManagerCoreAddressComputed?.address.toLowerCase()).to.eq(
251+
mintfeeManagerCoreAddress,
252+
);
253+
expect(claimableModuleAddressComputed?.address.toLowerCase()).to.eq(
254+
claimableModuleAddress,
255+
);
256+
});
195257
},
196258
);

packages/thirdweb/src/extensions/prebuilts/process-ref-deployments.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ type ProcessRefDeploymentsOptions = {
1717
paramValue: string | ImplementationConstructorParam;
1818
};
1919

20+
/**
21+
* Processes published contract references in constructor params. Deploys recursively if needed.
22+
* @returns Param value after processing references.
23+
* @internal
24+
*/
2025
export async function processRefDeployments(
2126
options: ProcessRefDeploymentsOptions,
2227
): Promise<string | string[]> {
@@ -42,7 +47,7 @@ export async function processRefDeployments(
4247
const salt =
4348
contracts[0]?.salt && contracts[0]?.salt.length > 0
4449
? contracts[0]?.salt
45-
: "thirdweb";
50+
: "";
4651

4752
const addr = await deployPublishedContract({
4853
client,
@@ -64,7 +69,7 @@ export async function processRefDeployments(
6469
const addressArray = [];
6570

6671
for (const c of contracts) {
67-
const salt = c?.salt && c?.salt.length > 0 ? c?.salt : "thirdweb";
72+
const salt = c?.salt && c?.salt.length > 0 ? c?.salt : "";
6873

6974
addressArray.push(
7075
await deployPublishedContract({

packages/thirdweb/src/utils/any-evm/compute-published-contract-deploy-info.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import type { Chain } from "../../chains/types.js";
33
import type { ThirdwebClient } from "../../client/client.js";
44
import { fetchPublishedContractMetadata } from "../../contract/deployment/publisher.js";
55
import { computeCreate2FactoryAddress } from "../../contract/deployment/utils/create-2-factory.js";
6+
import { computeRefDeployments } from "../../extensions/prebuilts/compute-ref-deployments.js";
7+
import type { ImplementationConstructorParam } from "../../extensions/prebuilts/process-ref-deployments.js";
68
import { encodeAbiParameters } from "../abi/encodeAbiParameters.js";
79
import { normalizeFunctionParams } from "../abi/normalizeFunctionParams.js";
810
import { ensureBytecodePrefix } from "../bytecode/prefix.js";
@@ -51,6 +53,23 @@ export async function computeDeploymentInfoFromMetadata(args: {
5153
constructorParams?: Record<string, unknown>;
5254
salt?: string;
5355
}) {
56+
const { client, chain, constructorParams, contractMetadata } = args;
57+
const definedConstructorParams =
58+
constructorParams || contractMetadata.constructorParams;
59+
let processedConstructorParams: Record<string, string | string[]> | undefined;
60+
if (definedConstructorParams) {
61+
processedConstructorParams = {};
62+
for (const key in definedConstructorParams) {
63+
processedConstructorParams[key] = await computeRefDeployments({
64+
client,
65+
chain,
66+
paramValue: definedConstructorParams[key] as
67+
| string
68+
| ImplementationConstructorParam,
69+
});
70+
}
71+
}
72+
5473
return computeDeploymentInfoFromBytecode({
5574
client: args.client,
5675
chain: args.chain,
@@ -60,7 +79,7 @@ export async function computeDeploymentInfoFromMetadata(args: {
6079
client: args.client,
6180
chain: args.chain,
6281
}),
63-
constructorParams: args.constructorParams,
82+
constructorParams: processedConstructorParams,
6483
salt: args.salt,
6584
});
6685
}

0 commit comments

Comments
 (0)