Skip to content

Commit 9319cec

Browse files
committed
bootstrap infra
1 parent 488c4e4 commit 9319cec

File tree

7 files changed

+442
-26
lines changed

7 files changed

+442
-26
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[
2+
"function initialize(address _owner) external"
3+
]

packages/thirdweb/src/assets/bootstrap.ts

Lines changed: 164 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { getContract } from "src/contract/contract.js";
22
import { assetInfraDeployedEvent } from "src/extensions/assets/__generated__/AssetInfraDeployer/events/AssetInfraDeployed.js";
33
import { deployInfraProxyDeterministic } from "src/extensions/assets/__generated__/AssetInfraDeployer/write/deployInfraProxyDeterministic.js";
4-
import { encodeInitialize } from "src/extensions/assets/__generated__/FeeManager/write/initialize.js";
4+
import { encodeInitialize as encodeFeeManagerInit } from "src/extensions/assets/__generated__/FeeManager/write/initialize.js";
5+
import { encodeInitialize as encodeRouterInit } from "src/extensions/assets/__generated__/Router/write/initialize.js";
56
import { isContractDeployed } from "src/utils/bytecode/is-contract-deployed.js";
67
import { keccak256 } from "src/utils/hashing/keccak256.js";
78
import { encodePacked } from "viem";
@@ -26,6 +27,64 @@ import {
2627
DEFAULT_SALT,
2728
IMPLEMENTATIONS,
2829
} from "./constants.js";
30+
import { deployInfraProxy } from "./deploy-infra-proxy.js";
31+
32+
export async function deployRouter(options: ClientAndChainAndAccount) {
33+
let [feeManager, marketSaleImpl] = await Promise.all([
34+
getDeployedFeeManager(options),
35+
getDeployedInfraContract({
36+
...options,
37+
contractId: "MarketSale",
38+
publisher: "0x6453a486d52e0EB6E79Ec4491038E2522a926936",
39+
}),
40+
]);
41+
42+
if (!feeManager) {
43+
feeManager = await deployFeeManager(options);
44+
}
45+
46+
if (!marketSaleImpl) {
47+
marketSaleImpl = await getOrDeployInfraContract({
48+
...options,
49+
contractId: "MarketSale",
50+
publisher: "0x6453a486d52e0EB6E79Ec4491038E2522a926936",
51+
});
52+
}
53+
54+
const assetFactory = await getDeployedAssetFactory(options);
55+
if (!assetFactory) {
56+
throw new Error(`Asset factory not found for chain: ${options.chain.id}`);
57+
}
58+
59+
const routerImpl = await getOrDeployInfraContract({
60+
...options,
61+
contractId: "Router",
62+
constructorParams: {
63+
_marketSaleImplementation: marketSaleImpl.address,
64+
_feeManager: feeManager,
65+
},
66+
publisher: "0x6453a486d52e0EB6E79Ec4491038E2522a926936",
67+
});
68+
69+
// encode init data
70+
const initData = encodeRouterInit({
71+
owner: DEFAULT_INFRA_ADMIN,
72+
});
73+
74+
const routerProxyAddress = await deployInfraProxy({
75+
...options,
76+
initData,
77+
extraData: "0x",
78+
implementationAddress: routerImpl.address,
79+
assetFactory,
80+
});
81+
82+
return getContract({
83+
client: options.client,
84+
chain: options.chain,
85+
address: routerProxyAddress,
86+
});
87+
}
2988

3089
export async function deployRewardLocker(options: ClientAndChainAndAccount) {
3190
let v3PositionManager = ZERO_ADDRESS;
@@ -52,6 +111,7 @@ export async function deployRewardLocker(options: ClientAndChainAndAccount) {
52111
_v3PositionManager: v3PositionManager,
53112
_v4PositionManager: v4PositionManager,
54113
},
114+
publisher: "0x6453a486d52e0EB6E79Ec4491038E2522a926936",
55115
});
56116
}
57117

@@ -66,10 +126,11 @@ export async function deployFeeManager(options: ClientAndChainAndAccount) {
66126
const feeManagerImpl = await getOrDeployInfraContract({
67127
...options,
68128
contractId: "FeeManager",
129+
publisher: "0x6453a486d52e0EB6E79Ec4491038E2522a926936",
69130
});
70131

71132
// encode init data
72-
const initData = encodeInitialize({
133+
const initData = encodeFeeManagerInit({
73134
owner: DEFAULT_INFRA_ADMIN,
74135
feeRecipient: DEFAULT_FEE_RECIPIENT,
75136
defaultFee: DEFAULT_FEE_BPS,
@@ -100,7 +161,13 @@ export async function deployFeeManager(options: ClientAndChainAndAccount) {
100161
);
101162
}
102163

103-
return decodedEvent[0]?.args.proxy;
164+
const feeManagerProxyAddress = decodedEvent[0]?.args.proxy;
165+
166+
return getContract({
167+
client: options.client,
168+
chain: options.chain,
169+
address: feeManagerProxyAddress,
170+
});
104171
}
105172

106173
async function deployAssetFactory(options: ClientAndChainAndAccount) {
@@ -114,6 +181,95 @@ async function deployAssetFactory(options: ClientAndChainAndAccount) {
114181
return getOrDeployInfraContract({
115182
...options,
116183
contractId: "AssetInfraDeployer",
184+
publisher: "0x6453a486d52e0EB6E79Ec4491038E2522a926936",
185+
});
186+
}
187+
188+
export async function getDeployedRouter(options: ClientAndChain) {
189+
const [feeManager, marketSaleImpl, assetFactory] = await Promise.all([
190+
getDeployedFeeManager(options),
191+
getDeployedInfraContract({
192+
...options,
193+
contractId: "MarketSale",
194+
publisher: "0x6453a486d52e0EB6E79Ec4491038E2522a926936",
195+
}),
196+
getDeployedAssetFactory(options),
197+
]);
198+
199+
if (!feeManager || !marketSaleImpl || !assetFactory) {
200+
return null;
201+
}
202+
203+
const routerImpl = await getDeployedInfraContract({
204+
...options,
205+
contractId: "Router",
206+
constructorParams: {
207+
_marketSaleImplementation: marketSaleImpl.address,
208+
_feeManager: feeManager,
209+
},
210+
publisher: "0x6453a486d52e0EB6E79Ec4491038E2522a926936",
211+
});
212+
213+
if (!routerImpl) {
214+
return null;
215+
}
216+
217+
const initCodeHash = getInitCodeHashERC1967(routerImpl.address);
218+
219+
const saltHash = keccak256(
220+
encodePacked(
221+
["bytes32", "address"],
222+
[keccakId(DEFAULT_SALT), DEFAULT_INFRA_ADMIN],
223+
),
224+
);
225+
226+
const hashedDeployInfo = keccak256(
227+
encodePacked(
228+
["bytes1", "address", "bytes32", "bytes32"],
229+
["0xff", assetFactory.address, saltHash, initCodeHash],
230+
),
231+
);
232+
233+
const routerProxyAddress = `0x${hashedDeployInfo.slice(26)}`;
234+
const routerProxy = getContract({
235+
client: options.client,
236+
chain: options.chain,
237+
address: routerProxyAddress,
238+
});
239+
240+
if (!(await isContractDeployed(routerProxy))) {
241+
return null;
242+
}
243+
244+
return routerProxy;
245+
}
246+
247+
export async function getDeployedRewardLocker(options: ClientAndChain) {
248+
let v3PositionManager = ZERO_ADDRESS;
249+
let v4PositionManager = ZERO_ADDRESS;
250+
251+
const implementations = IMPLEMENTATIONS[options.chain.id];
252+
253+
if (implementations) {
254+
v3PositionManager = implementations.V3PositionManager || ZERO_ADDRESS;
255+
v4PositionManager = implementations.V4PositionManager || ZERO_ADDRESS;
256+
}
257+
258+
const feeManager = await getDeployedFeeManager(options);
259+
260+
if (!feeManager) {
261+
return null;
262+
}
263+
264+
return await getDeployedInfraContract({
265+
...options,
266+
contractId: "RewardLocker",
267+
constructorParams: {
268+
_feeManager: feeManager,
269+
_v3PositionManager: v3PositionManager,
270+
_v4PositionManager: v4PositionManager,
271+
},
272+
publisher: "0x6453a486d52e0EB6E79Ec4491038E2522a926936",
117273
});
118274
}
119275

@@ -123,6 +279,7 @@ export async function getDeployedFeeManager(options: ClientAndChain) {
123279
getDeployedInfraContract({
124280
...options,
125281
contractId: "FeeManager",
282+
publisher: "0x6453a486d52e0EB6E79Ec4491038E2522a926936",
126283
}),
127284
]);
128285

@@ -157,21 +314,22 @@ export async function getDeployedFeeManager(options: ClientAndChain) {
157314
return null;
158315
}
159316

160-
return feeManagerProxyAddress;
317+
return feeManagerProxy;
161318
}
162319

163-
async function getDeployedAssetFactory(args: ClientAndChain) {
320+
export async function getDeployedAssetFactory(args: ClientAndChain) {
164321
const assetFactory = await getDeployedInfraContract({
165322
...args,
166323
contractId: "AssetInfraDeployer",
324+
publisher: "0x6453a486d52e0EB6E79Ec4491038E2522a926936",
167325
});
168326
if (!assetFactory) {
169327
return null;
170328
}
171329
return assetFactory;
172330
}
173331

174-
function getInitCodeHashERC1967(implementation: string) {
332+
export function getInitCodeHashERC1967(implementation: string) {
175333
// See `initCodeHashERC1967` - LibClone {https://github.com/vectorized/solady/blob/main/src/utils/LibClone.sol}
176334
return keccak256(
177335
`0x603d3d8160223d3973${implementation.toLowerCase().replace(/^0x/, "")}60095155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3`,
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { describe, expect, it } from "vitest";
2+
import { ANVIL_CHAIN } from "../../test/src/chains.js";
3+
import { TEST_CLIENT } from "../../test/src/test-clients.js";
4+
import { TEST_ACCOUNT_A } from "../../test/src/test-wallets.js";
5+
import { deployFeeManager, getDeployedFeeManager } from "./bootstrap.js";
6+
7+
describe.runIf(process.env.TW_SECRET_KEY)("bootstrap asset infra", () => {
8+
it("should bootstrap fee manager", async () => {
9+
const feeManager = await deployFeeManager({
10+
chain: ANVIL_CHAIN,
11+
client: TEST_CLIENT,
12+
account: TEST_ACCOUNT_A,
13+
});
14+
15+
const expectedFeeManager = await getDeployedFeeManager({
16+
chain: ANVIL_CHAIN,
17+
client: TEST_CLIENT,
18+
});
19+
20+
expect(expectedFeeManager).toBeDefined();
21+
expect(feeManager.address.toLowerCase()).to.equal(
22+
expectedFeeManager?.address?.toLowerCase(),
23+
);
24+
});
25+
});

packages/thirdweb/src/assets/create-token.ts

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import type { Hex } from "viem";
22
import type { ThirdwebClient } from "../client/client.js";
33
import { NATIVE_TOKEN_ADDRESS, ZERO_ADDRESS } from "../constants/addresses.js";
4-
import { getContract } from "../contract/contract.js";
54
import { createAsset } from "../extensions/assets/__generated__/AssetEntrypointERC20/write/createAsset.js";
65
import { encodeInitialize } from "../extensions/assets/__generated__/ERC20Asset/write/initialize.js";
76
import { eth_blockNumber } from "../rpc/actions/eth_blockNumber.js";
@@ -18,7 +17,7 @@ import {
1817
DEFAULT_POOL_FEE,
1918
DEFAULT_POOL_INITIAL_TICK,
2019
} from "./constants.js";
21-
import { getEntrypointERC20 } from "./get-entrypoint-erc20.js";
20+
import { getOrDeployEntrypointERC20 } from "./get-entrypoint-erc20.js";
2221

2322
export type TokenParams = {
2423
name: string;
@@ -46,7 +45,7 @@ export type CreateTokenOptions = ClientAndChainAndAccount & {
4645
};
4746

4847
export async function createToken(options: CreateTokenOptions) {
49-
const { chain, client, account, params, poolConfig } = options;
48+
const { client, account, params, poolConfig } = options;
5049

5150
const creator = params.owner || account.address;
5251

@@ -68,12 +67,7 @@ export async function createToken(options: CreateTokenOptions) {
6867
size: 32,
6968
});
7069

71-
const entrypointAddress = await getEntrypointERC20(chain);
72-
const entrypoint = getContract({
73-
client,
74-
address: entrypointAddress,
75-
chain,
76-
});
70+
const entrypoint = await getOrDeployEntrypointERC20(options);
7771

7872
const hookData = poolConfig ? encodePoolConfig(poolConfig) : "0x";
7973

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { assetInfraDeployedEvent } from "src/extensions/assets/__generated__/AssetInfraDeployer/events/AssetInfraDeployed.js";
2+
import { deployInfraProxyDeterministic } from "src/extensions/assets/__generated__/AssetInfraDeployer/write/deployInfraProxyDeterministic.js";
3+
import type { Hex } from "viem";
4+
import type { ThirdwebContract } from "../contract/contract.js";
5+
import {} from "../contract/deployment/utils/create-2-factory.js";
6+
import { parseEventLogs } from "../event/actions/parse-logs.js";
7+
import { sendAndConfirmTransaction } from "../transaction/actions/send-and-confirm-transaction.js";
8+
import { keccakId } from "../utils/any-evm/keccak-id.js";
9+
import type { ClientAndChainAndAccount } from "../utils/types.js";
10+
import { DEFAULT_SALT } from "./constants.js";
11+
12+
export async function deployInfraProxy(
13+
options: ClientAndChainAndAccount & {
14+
assetFactory: ThirdwebContract;
15+
implementationAddress: string;
16+
initData: Hex;
17+
extraData: Hex;
18+
},
19+
) {
20+
const transaction = deployInfraProxyDeterministic({
21+
contract: options.assetFactory,
22+
implementation: options.implementationAddress,
23+
data: options.initData,
24+
extraData: options.extraData,
25+
salt: keccakId(DEFAULT_SALT),
26+
});
27+
28+
const receipt = await sendAndConfirmTransaction({
29+
transaction,
30+
account: options.account,
31+
});
32+
const proxyEvent = assetInfraDeployedEvent();
33+
const decodedEvent = parseEventLogs({
34+
events: [proxyEvent],
35+
logs: receipt.logs,
36+
});
37+
38+
if (decodedEvent.length === 0 || !decodedEvent[0]) {
39+
throw new Error(
40+
`No AssetInfraDeployed event found in transaction: ${receipt.transactionHash}`,
41+
);
42+
}
43+
44+
return decodedEvent[0]?.args.proxy;
45+
}

0 commit comments

Comments
 (0)