Skip to content

Commit 488c4e4

Browse files
committed
wip bootstrap
1 parent 5298dab commit 488c4e4

File tree

7 files changed

+599
-0
lines changed

7 files changed

+599
-0
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[
2+
"function deployInfraProxyDeterministic(address implementation, bytes data, bytes32 salt, bytes extraData) public returns (address deployedProxy)",
3+
"event AssetInfraDeployed(address indexed implementation, address indexed proxy, bytes32 inputSalt, bytes data, bytes extraData)",
4+
"error ProxyDeploymentFailed()"
5+
]
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[
2+
"function initialize(address _owner, address _feeRecipient, uint96 _defaultFee) external"
3+
]
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
import { getContract } from "src/contract/contract.js";
2+
import { assetInfraDeployedEvent } from "src/extensions/assets/__generated__/AssetInfraDeployer/events/AssetInfraDeployed.js";
3+
import { deployInfraProxyDeterministic } from "src/extensions/assets/__generated__/AssetInfraDeployer/write/deployInfraProxyDeterministic.js";
4+
import { encodeInitialize } from "src/extensions/assets/__generated__/FeeManager/write/initialize.js";
5+
import { isContractDeployed } from "src/utils/bytecode/is-contract-deployed.js";
6+
import { keccak256 } from "src/utils/hashing/keccak256.js";
7+
import { encodePacked } from "viem";
8+
import { ZERO_ADDRESS } from "~test/addresses.js";
9+
import { getOrDeployInfraContract } from "../contract/deployment/utils/bootstrap.js";
10+
import {
11+
deployCreate2Factory,
12+
getDeployedCreate2Factory,
13+
} from "../contract/deployment/utils/create-2-factory.js";
14+
import { getDeployedInfraContract } from "../contract/deployment/utils/infra.js";
15+
import { parseEventLogs } from "../event/actions/parse-logs.js";
16+
import { sendAndConfirmTransaction } from "../transaction/actions/send-and-confirm-transaction.js";
17+
import { keccakId } from "../utils/any-evm/keccak-id.js";
18+
import type {
19+
ClientAndChain,
20+
ClientAndChainAndAccount,
21+
} from "../utils/types.js";
22+
import {
23+
DEFAULT_FEE_BPS,
24+
DEFAULT_FEE_RECIPIENT,
25+
DEFAULT_INFRA_ADMIN,
26+
DEFAULT_SALT,
27+
IMPLEMENTATIONS,
28+
} from "./constants.js";
29+
30+
export async function deployRewardLocker(options: ClientAndChainAndAccount) {
31+
let v3PositionManager = ZERO_ADDRESS;
32+
let v4PositionManager = ZERO_ADDRESS;
33+
34+
const implementations = IMPLEMENTATIONS[options.chain.id];
35+
36+
if (implementations) {
37+
v3PositionManager = implementations.V3PositionManager || ZERO_ADDRESS;
38+
v4PositionManager = implementations.V4PositionManager || ZERO_ADDRESS;
39+
}
40+
41+
let feeManager = await getDeployedFeeManager(options);
42+
43+
if (!feeManager) {
44+
feeManager = await deployFeeManager(options);
45+
}
46+
47+
return await getOrDeployInfraContract({
48+
...options,
49+
contractId: "RewardLocker",
50+
constructorParams: {
51+
_feeManager: feeManager,
52+
_v3PositionManager: v3PositionManager,
53+
_v4PositionManager: v4PositionManager,
54+
},
55+
});
56+
}
57+
58+
export async function deployFeeManager(options: ClientAndChainAndAccount) {
59+
// asset factory
60+
let assetFactory = await getDeployedAssetFactory(options);
61+
if (!assetFactory) {
62+
assetFactory = await deployAssetFactory(options);
63+
}
64+
65+
// fee manager implementation
66+
const feeManagerImpl = await getOrDeployInfraContract({
67+
...options,
68+
contractId: "FeeManager",
69+
});
70+
71+
// encode init data
72+
const initData = encodeInitialize({
73+
owner: DEFAULT_INFRA_ADMIN,
74+
feeRecipient: DEFAULT_FEE_RECIPIENT,
75+
defaultFee: DEFAULT_FEE_BPS,
76+
});
77+
78+
// fee manager proxy deployment
79+
const transaction = deployInfraProxyDeterministic({
80+
contract: assetFactory,
81+
implementation: feeManagerImpl.address,
82+
data: initData,
83+
extraData: "0x",
84+
salt: keccakId(DEFAULT_SALT),
85+
});
86+
87+
const receipt = await sendAndConfirmTransaction({
88+
transaction,
89+
account: options.account,
90+
});
91+
const proxyEvent = assetInfraDeployedEvent();
92+
const decodedEvent = parseEventLogs({
93+
events: [proxyEvent],
94+
logs: receipt.logs,
95+
});
96+
97+
if (decodedEvent.length === 0 || !decodedEvent[0]) {
98+
throw new Error(
99+
`No AssetInfraDeployed event found in transaction: ${receipt.transactionHash}`,
100+
);
101+
}
102+
103+
return decodedEvent[0]?.args.proxy;
104+
}
105+
106+
async function deployAssetFactory(options: ClientAndChainAndAccount) {
107+
// create2 factory
108+
const create2Factory = await getDeployedCreate2Factory(options);
109+
if (!create2Factory) {
110+
await deployCreate2Factory(options);
111+
}
112+
113+
// asset factory
114+
return getOrDeployInfraContract({
115+
...options,
116+
contractId: "AssetInfraDeployer",
117+
});
118+
}
119+
120+
export async function getDeployedFeeManager(options: ClientAndChain) {
121+
const [assetFactory, feeManagerImpl] = await Promise.all([
122+
getDeployedAssetFactory(options),
123+
getDeployedInfraContract({
124+
...options,
125+
contractId: "FeeManager",
126+
}),
127+
]);
128+
129+
if (!assetFactory || !feeManagerImpl) {
130+
return null;
131+
}
132+
133+
const initCodeHash = getInitCodeHashERC1967(feeManagerImpl.address);
134+
135+
const saltHash = keccak256(
136+
encodePacked(
137+
["bytes32", "address"],
138+
[keccakId(DEFAULT_SALT), DEFAULT_INFRA_ADMIN],
139+
),
140+
);
141+
142+
const hashedDeployInfo = keccak256(
143+
encodePacked(
144+
["bytes1", "address", "bytes32", "bytes32"],
145+
["0xff", assetFactory.address, saltHash, initCodeHash],
146+
),
147+
);
148+
149+
const feeManagerProxyAddress = `0x${hashedDeployInfo.slice(26)}`;
150+
const feeManagerProxy = getContract({
151+
client: options.client,
152+
chain: options.chain,
153+
address: feeManagerProxyAddress,
154+
});
155+
156+
if (!(await isContractDeployed(feeManagerProxy))) {
157+
return null;
158+
}
159+
160+
return feeManagerProxyAddress;
161+
}
162+
163+
async function getDeployedAssetFactory(args: ClientAndChain) {
164+
const assetFactory = await getDeployedInfraContract({
165+
...args,
166+
contractId: "AssetInfraDeployer",
167+
});
168+
if (!assetFactory) {
169+
return null;
170+
}
171+
return assetFactory;
172+
}
173+
174+
function getInitCodeHashERC1967(implementation: string) {
175+
// See `initCodeHashERC1967` - LibClone {https://github.com/vectorized/solady/blob/main/src/utils/LibClone.sol}
176+
return keccak256(
177+
`0x603d3d8160223d3973${implementation.toLowerCase().replace(/^0x/, "")}60095155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3`,
178+
);
179+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
export const DEFAULT_MAX_SUPPLY_ERC20 = 10_000_000_000n;
22
export const DEFAULT_POOL_FEE = 10000;
33
export const DEFAULT_POOL_INITIAL_TICK = 230200;
4+
export const DEFAULT_INFRA_ADMIN = "0x1a472863cf21d5aa27f417df9140400324c48f22";
5+
export const DEFAULT_FEE_RECIPIENT =
6+
"0x1af20c6b23373350ad464700b5965ce4b0d2ad94";
7+
export const DEFAULT_FEE_BPS = 50n;
8+
export const DEFAULT_SALT = "thirdweb";
49

510
export const IMPLEMENTATIONS: Record<number, Record<string, string>> = {
611
[84532]: {
712
AssetEntrypointERC20: "0x79C1236cFe59f1f088A15Da08b0D8667387d9703",
13+
V3PositionManager: "",
14+
V4PositionManager: "",
815
},
916
};

packages/thirdweb/src/extensions/assets/__generated__/AssetInfraDeployer/events/AssetInfraDeployed.ts

Lines changed: 49 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)