Repository navigation
Expand file tree
/
Copy pathDeployToken.s.sol
More file actions
138 lines (118 loc) · 7.26 KB
/
Copy pathDeployToken.s.sol
File metadata and controls
138 lines (118 loc) · 7.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
import {Script, console} from "forge-std/Script.sol";
import {HelperUtils} from "../utils/HelperUtils.s.sol"; // Utility functions for JSON parsing and chain info
import {HelperConfig} from "../HelperConfig.s.sol"; // Network configuration helper
import {DeploymentRecorder} from "../utils/DeploymentRecorder.s.sol";
import {RegistryWriter} from "../../src/utils/RegistryWriter.sol";
import {BaseERC20} from "@chainlink/contracts-ccip/contracts/tokens/BaseERC20.sol";
import {CrossChainToken} from "@chainlink/contracts-ccip/contracts/tokens/CrossChainToken.sol";
import {OutcomeLog} from "../../src/base/OutcomeLog.sol";
/// @notice Deploys a cross-chain ERC20 token (CrossChainToken) and records it in the address registry.
contract DeployToken is Script {
HelperConfig public helperConfig;
struct TokenConfig {
string name;
string symbol;
uint8 decimals;
uint256 maxSupply;
uint256 preMint;
address preMintRecipient;
address ccipAdmin;
}
function run() external {
// Initialize HelperConfig
helperConfig = new HelperConfig();
uint256 chainId = block.chainid;
string memory chainName = helperConfig.getChainName(chainId);
string memory selectorName = helperConfig.getSelectorName(chainId);
console.log("");
console.log("========================================");
console.log(unicode"🪙 Deploy Token");
console.log("========================================");
console.log(string.concat("Chain: ", chainName));
console.log(string.concat("Action: ", "Deploy token"));
console.log("========================================");
console.log("");
string memory root = vm.projectRoot();
string memory tokenConfigPath = string.concat(root, "/script/input/token.json");
TokenConfig memory tokenConfig = _loadTokenConfig(tokenConfigPath);
// Refuse to redeploy over a live registry entry (FORCE_REDEPLOY=true overrides). Keyed on the
// unique deployment name so distinct symbols on one chain never collide.
RegistryWriter._guard(selectorName, DeploymentRecorder._tokenName(tokenConfig.symbol));
vm.startBroadcast();
// forge-lint: disable-next-line(unused-return) - the discarded CallerMode is necessarily a broadcast mode - this sits inside an open startBroadcast
(, address broadcaster,) = vm.readCallers();
// Default preMintRecipient to broadcaster if a pre-mint is requested but no recipient was specified
if (tokenConfig.preMint > 0 && tokenConfig.preMintRecipient == address(0)) {
tokenConfig.preMintRecipient = broadcaster;
}
console.log(string.concat(" Pre-mint Recipient: ", vm.toString(tokenConfig.preMintRecipient)));
// ccipAdmin address(0) resolves to msg.sender (broadcaster) inside BaseERC20 constructor
console.log(
string.concat(
" CCIP Admin: ",
tokenConfig.ccipAdmin == address(0) ? vm.toString(broadcaster) : vm.toString(tokenConfig.ccipAdmin)
)
);
console.log("");
console.log(
string.concat("\n[Step 1] Deploying ", tokenConfig.name, " (", tokenConfig.symbol, ") on ", chainName)
);
BaseERC20.ConstructorParams memory params = BaseERC20.ConstructorParams({
name: tokenConfig.name,
symbol: tokenConfig.symbol,
maxSupply: tokenConfig.maxSupply,
preMint: tokenConfig.preMint,
preMintRecipient: tokenConfig.preMintRecipient,
decimals: tokenConfig.decimals,
ccipAdmin: tokenConfig.ccipAdmin
});
CrossChainToken token = new CrossChainToken(params, broadcaster, address(0));
address tokenAddress = address(token);
console.log(string.concat("Token deployed at: ", vm.toString(tokenAddress)));
console.log(helperConfig.getExplorerUrl(chainId, "/address/", tokenAddress));
// Grant mint and burn roles to the specified address
address rolesRecipient = vm.envOr("ROLES_RECIPIENT", broadcaster); // Default to deployer if not set
console.log(string.concat("\n[Step 2] Granting mint and burn roles to: ", vm.toString(rolesRecipient)));
token.grantMintAndBurnRoles(rolesRecipient);
OutcomeLog._sending(string.concat("grant mint and burn roles to ", vm.toString(rolesRecipient)));
vm.stopBroadcast();
string memory chainNameIdentifier = helperConfig.getNetworkConfig(chainId).chainNameIdentifier;
console.log("");
console.log("========================================");
OutcomeLog._sending(string.concat("deploy the token on ", chainName));
console.log("========================================");
console.log(string.concat("Token Address: ", vm.toString(tokenAddress)));
console.log(helperConfig.getExplorerUrl(chainId, "/address/", tokenAddress));
console.log("");
// Single writer: one call emits the detailed ledger file AND records the address in the
// registry (deployments[{symbol}_Token] + active.token).
DeploymentRecorder._recordToken(vm, selectorName, chainNameIdentifier, tokenConfig.symbol, tokenAddress);
console.log("");
DeploymentRecorder._logResolution(vm, string.concat(chainNameIdentifier, "_TOKEN"), tokenAddress);
console.log("========================================");
console.log("");
}
/// @dev Reads token parameters from env vars (with JSON config as fallback), logs them,
/// and returns a TokenConfig struct ready for deployment.
/// @param tokenConfigPath Absolute path to the token JSON config file (e.g. `script/input/token.json`)
/// @return config Populated TokenConfig struct with name, symbol, decimals, maxSupply, and preMint
function _loadTokenConfig(string memory tokenConfigPath) internal view returns (TokenConfig memory config) {
config.name = vm.envOr("TOKEN_NAME", HelperUtils._getStringFromJson(vm, tokenConfigPath, ".name"));
config.symbol = vm.envOr("TOKEN_SYMBOL", HelperUtils._getStringFromJson(vm, tokenConfigPath, ".symbol"));
config.decimals =
uint8(vm.envOr("TOKEN_DECIMALS", HelperUtils._getUintFromJson(vm, tokenConfigPath, ".decimals")));
config.maxSupply = vm.envOr("TOKEN_MAX_SUPPLY", HelperUtils._getUintFromJson(vm, tokenConfigPath, ".maxSupply"));
config.preMint = vm.envOr("TOKEN_PRE_MINT", HelperUtils._getUintFromJson(vm, tokenConfigPath, ".preMint"));
config.preMintRecipient = vm.envOr("TOKEN_PRE_MINT_RECIPIENT", address(0));
config.ccipAdmin = vm.envOr("CCIP_ADMIN_ADDRESS", address(0));
console.log("Token Parameters:");
console.log(string.concat(" Name: ", config.name));
console.log(string.concat(" Symbol: ", config.symbol));
console.log(string.concat(" Decimals: ", vm.toString(config.decimals)));
console.log(string.concat(" Max Supply: ", vm.toString(config.maxSupply)));
console.log(string.concat(" Pre-mint: ", vm.toString(config.preMint)));
console.log("");
}
}