@mapprotocol/common-contracts 0.3.2 → 0.4.1
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/README.md +30 -100
- package/package.json +14 -1
- package/script/base/Base.s.sol +231 -0
- package/script/base/interfaces/IFactory.sol +7 -0
- package/utils/deployer.ts +15 -3
- package/utils/dist/deployer.d.ts +12 -0
- package/utils/dist/verifier.d.ts +10 -0
- package/utils/dist/verifier.js +71 -35
- package/utils/verifier.ts +85 -43
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ npm install @mapprotocol/common-contracts
|
|
|
12
12
|
|
|
13
13
|
### BaseImplementation
|
|
14
14
|
|
|
15
|
-
Abstract base for all upgradeable protocol contracts
|
|
15
|
+
Abstract base for all upgradeable protocol contracts (UUPS + Pausable + AccessManaged).
|
|
16
16
|
|
|
17
17
|
```solidity
|
|
18
18
|
import "@mapprotocol/common-contracts/contracts/base/BaseImplementation.sol";
|
|
@@ -24,12 +24,6 @@ contract MyContract is BaseImplementation {
|
|
|
24
24
|
}
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
**Includes:**
|
|
28
|
-
- `UUPSUpgradeable` — proxy upgrade pattern
|
|
29
|
-
- `PausableUpgradeable` — emergency pause via `trigger()`
|
|
30
|
-
- `AccessManagedUpgradeable` — role-based function access
|
|
31
|
-
- `getImplementation()` — read current implementation address
|
|
32
|
-
|
|
33
27
|
### AuthorityManager
|
|
34
28
|
|
|
35
29
|
Extended `AccessManager` with enumerable role members. One instance per chain controls all protocol contracts.
|
|
@@ -38,82 +32,49 @@ Extended `AccessManager` with enumerable role members. One instance per chain co
|
|
|
38
32
|
import "@mapprotocol/common-contracts/contracts/AuthorityManager.sol";
|
|
39
33
|
```
|
|
40
34
|
|
|
41
|
-
**Key functions:**
|
|
42
|
-
- `grantRole(roleId, account, delay)` — assign role
|
|
43
|
-
- `revokeRole(roleId, account)` — remove role
|
|
44
|
-
- `setTargetFunctionRole(target, selectors, roleId)` — restrict contract functions to a role
|
|
45
|
-
- `getRoleMembers(roleId)` — list all members of a role
|
|
46
|
-
|
|
47
35
|
## TypeScript Utilities
|
|
48
36
|
|
|
49
|
-
### Deployment
|
|
50
|
-
|
|
51
37
|
```typescript
|
|
52
|
-
import {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
// With custom suffix and path
|
|
58
|
-
const addr = await getDeploymentByKey("Bsc", "Gateway", { suffix: "main", basePath: "./my-deployments" });
|
|
59
|
-
|
|
60
|
-
// Save deployment
|
|
61
|
-
await saveDeployment("Bsc", "Gateway", "0x...");
|
|
38
|
+
import { createDeployer } from "@mapprotocol/common-contracts/utils/deployer";
|
|
39
|
+
import { getDeploymentByKey, saveDeployment } from "@mapprotocol/common-contracts/utils/deployRecord";
|
|
40
|
+
import { TronClient, tronToHex, tronFromHex } from "@mapprotocol/common-contracts/utils/tronHelper";
|
|
41
|
+
import { verify } from "@mapprotocol/common-contracts/utils/verifier";
|
|
42
|
+
import { addressToHex } from "@mapprotocol/common-contracts/utils/addressCodec";
|
|
62
43
|
```
|
|
63
44
|
|
|
64
|
-
###
|
|
45
|
+
### Quick Start
|
|
65
46
|
|
|
66
47
|
```typescript
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
48
|
+
// Unified deployer — auto-routes EVM / Tron
|
|
49
|
+
const deployer = createDeployer(hre, { autoVerify: true });
|
|
50
|
+
let result = await deployer.deploy("Gateway", [bridge, owner, wtoken]);
|
|
51
|
+
let proxy = await deployer.deployProxy("Gateway", [admin]);
|
|
52
|
+
|
|
53
|
+
// Deploy record
|
|
54
|
+
const addr = getDeploymentByKey("Bsc", "Gateway", { env: "prod" });
|
|
55
|
+
saveDeployment("Bsc", "Gateway", "0x...", { env: "prod" });
|
|
56
|
+
|
|
57
|
+
// Tron client
|
|
58
|
+
let client = TronClient.fromHre(hre);
|
|
59
|
+
let gw = await client.getContract(hre.artifacts, "Gateway", addr);
|
|
60
|
+
await gw.setWtoken(client.toHex(wtoken)).sendAndWait();
|
|
61
|
+
let val = await gw.wToken().call();
|
|
80
62
|
```
|
|
81
63
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
```typescript
|
|
85
|
-
import { addressToHex, isTronAddress, isSolanaChain } from "@mapprotocol/common-contracts/utils/addressCodec";
|
|
86
|
-
|
|
87
|
-
// Auto-detect format and convert to hex
|
|
88
|
-
addressToHex("0xAbC..."); // EVM -> lowercase hex
|
|
89
|
-
addressToHex("TXyz..."); // Tron -> extract 20-byte address
|
|
90
|
-
addressToHex("So11111..."); // Solana -> full base58 decode
|
|
91
|
-
|
|
92
|
-
// Type checks
|
|
93
|
-
isTronAddress("TXyz..."); // true (validates 0x41 prefix byte)
|
|
94
|
-
isSolanaChain("Sol"); // true
|
|
95
|
-
```
|
|
64
|
+
See JSDoc in each module's source for full API details and edge cases.
|
|
96
65
|
|
|
97
66
|
## Forge Script Base
|
|
98
67
|
|
|
99
|
-
For monorepo projects, `script/Base.s.sol` provides deployment primitives:
|
|
100
|
-
|
|
101
68
|
```solidity
|
|
102
|
-
import {BaseScript} from "
|
|
69
|
+
import {BaseScript} from "@mapprotocol/common-contracts/script/base/Base.s.sol";
|
|
103
70
|
|
|
104
71
|
contract MyDeploy is BaseScript {
|
|
105
72
|
function run() public broadcast {
|
|
106
|
-
|
|
73
|
+
(address proxy, address impl) = deployProxy(type(MyContract).creationCode, initData);
|
|
107
74
|
address addr = deployByFactory("my_salt", type(MyContract).creationCode, abi.encode(arg));
|
|
108
|
-
|
|
109
|
-
// Check factory availability
|
|
110
|
-
require(isFactoryAvailable(), "no factory on this chain");
|
|
111
|
-
|
|
112
|
-
// UUPS proxy upgrade
|
|
113
|
-
upgradeProxy(proxyAddr, newImplAddr);
|
|
114
|
-
|
|
115
|
-
// Deploy new impl + upgrade in one step
|
|
116
75
|
deployAndUpgrade(proxyAddr, type(MyContractV2).creationCode);
|
|
76
|
+
address relay = readDeployment("Relay");
|
|
77
|
+
saveDeployment("Gateway", addr);
|
|
117
78
|
}
|
|
118
79
|
}
|
|
119
80
|
```
|
|
@@ -121,43 +82,12 @@ contract MyDeploy is BaseScript {
|
|
|
121
82
|
## Development
|
|
122
83
|
|
|
123
84
|
```bash
|
|
124
|
-
#
|
|
125
|
-
|
|
126
|
-
npm run build:
|
|
127
|
-
|
|
128
|
-
# Test
|
|
129
|
-
forge test
|
|
130
|
-
forge test --gas-report
|
|
131
|
-
|
|
132
|
-
# Format
|
|
133
|
-
forge fmt
|
|
134
|
-
|
|
135
|
-
# Publish
|
|
136
|
-
npm run prepublishOnly # clean + build + typecheck
|
|
137
|
-
npm publish
|
|
85
|
+
forge build && forge test # Solidity
|
|
86
|
+
npm run build:hardhat # Hardhat + TypeChain
|
|
87
|
+
npm run build:utils # TypeScript utilities
|
|
88
|
+
npm run prepublishOnly && npm publish
|
|
138
89
|
```
|
|
139
90
|
|
|
140
|
-
## Package Contents
|
|
141
|
-
|
|
142
|
-
| Path | Description |
|
|
143
|
-
|------|-------------|
|
|
144
|
-
| `contracts/**/*.sol` | Solidity source files |
|
|
145
|
-
| `artifacts/**/*.json` | Compiled ABI + bytecode |
|
|
146
|
-
| `typechain-types/**/*` | TypeChain generated types |
|
|
147
|
-
| `utils/*.ts` | Shared TypeScript utilities |
|
|
148
|
-
|
|
149
|
-
## Requirements
|
|
150
|
-
|
|
151
|
-
- Solidity ^0.8.20
|
|
152
|
-
- Node.js >= 18
|
|
153
|
-
- OpenZeppelin Contracts 5.4.0
|
|
154
|
-
|
|
155
91
|
## License
|
|
156
92
|
|
|
157
93
|
MIT
|
|
158
|
-
|
|
159
|
-
## Links
|
|
160
|
-
|
|
161
|
-
- [Repository](https://github.com/mapprotocol/mapo-contracts-v2/tree/main/common)
|
|
162
|
-
- [Issues](https://github.com/mapprotocol/mapo-contracts-v2/issues)
|
|
163
|
-
- [MAP Protocol](https://mapprotocol.io)
|
package/package.json
CHANGED
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mapprotocol/common-contracts",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Common contracts for MAP Protocol",
|
|
5
5
|
"main": "typechain-types/index.js",
|
|
6
6
|
"types": "typechain-types/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./typechain-types/index.d.ts",
|
|
10
|
+
"default": "./typechain-types/index.js"
|
|
11
|
+
},
|
|
12
|
+
"./utils/*": {
|
|
13
|
+
"types": "./utils/dist/*.d.ts",
|
|
14
|
+
"default": "./utils/dist/*.js"
|
|
15
|
+
},
|
|
16
|
+
"./contracts/*": "./contracts/*",
|
|
17
|
+
"./script/*": "./script/*"
|
|
18
|
+
},
|
|
7
19
|
"files": [
|
|
8
20
|
"contracts/**/*.sol",
|
|
9
21
|
"artifacts/contracts/**/*.json",
|
|
@@ -11,6 +23,7 @@
|
|
|
11
23
|
"typechain-types/**/*",
|
|
12
24
|
"utils/**/*.ts",
|
|
13
25
|
"utils/dist/**/*",
|
|
26
|
+
"script/base/**/*.sol",
|
|
14
27
|
"README.md"
|
|
15
28
|
],
|
|
16
29
|
"repository": {
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.20;
|
|
3
|
+
|
|
4
|
+
import {Script, stdJson, console} from "forge-std/Script.sol";
|
|
5
|
+
import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
|
|
6
|
+
import {IFactory} from "./interfaces/IFactory.sol";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @title BaseScript
|
|
10
|
+
* @notice Base contract for deployment scripts with full lifecycle support
|
|
11
|
+
* @dev Inherit this in your deployment scripts.
|
|
12
|
+
*
|
|
13
|
+
* Features:
|
|
14
|
+
* - Private key management (mainnet / testnet)
|
|
15
|
+
* - Direct deployment and CREATE2 factory deployment
|
|
16
|
+
* - Proxy deployment (implementation + ERC1967Proxy)
|
|
17
|
+
* - UUPS proxy upgrade
|
|
18
|
+
* - deploy.json record read/write
|
|
19
|
+
*
|
|
20
|
+
* Usage:
|
|
21
|
+
* contract MyDeploy is BaseScript {
|
|
22
|
+
* function run() public broadcast {
|
|
23
|
+
* // Direct deploy
|
|
24
|
+
* address impl = deployDirect(type(MyContract).creationCode, abi.encode(arg));
|
|
25
|
+
*
|
|
26
|
+
* // Factory deploy (deterministic address)
|
|
27
|
+
* address addr = deployByFactory("my_salt", type(MyContract).creationCode, abi.encode(arg));
|
|
28
|
+
*
|
|
29
|
+
* // Proxy deploy (impl + proxy)
|
|
30
|
+
* (address proxy, address impl) = deployProxy(type(MyContract).creationCode, initData);
|
|
31
|
+
* (address proxy, address impl) = deployProxyByFactory("my_salt", type(MyContract).creationCode, initData);
|
|
32
|
+
*
|
|
33
|
+
* // Upgrade
|
|
34
|
+
* upgradeProxy(proxy, newImplAddr);
|
|
35
|
+
* address newImpl = deployAndUpgrade(proxy, type(MyContractV2).creationCode);
|
|
36
|
+
*
|
|
37
|
+
* // Deploy record
|
|
38
|
+
* address relay = readDeployment("Relay");
|
|
39
|
+
* saveDeployment("Gateway", addr);
|
|
40
|
+
* }
|
|
41
|
+
* }
|
|
42
|
+
*/
|
|
43
|
+
abstract contract BaseScript is Script {
|
|
44
|
+
IFactory constant private FACTORY = IFactory(0x6258e4d2950757A749a4d4683A7342261ce12471);
|
|
45
|
+
using stdJson for string;
|
|
46
|
+
|
|
47
|
+
address internal broadcaster;
|
|
48
|
+
uint256 private broadcasterPK;
|
|
49
|
+
|
|
50
|
+
constructor() {
|
|
51
|
+
uint256 privateKey;
|
|
52
|
+
if (block.chainid == 212) {
|
|
53
|
+
privateKey = vm.envUint("TESTNET_PRIVATE_KEY");
|
|
54
|
+
} else {
|
|
55
|
+
privateKey = vm.envUint("PRIVATE_KEY");
|
|
56
|
+
}
|
|
57
|
+
broadcaster = vm.addr(privateKey);
|
|
58
|
+
broadcasterPK = privateKey;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
modifier broadcast() {
|
|
62
|
+
vm.startBroadcast(broadcasterPK);
|
|
63
|
+
_;
|
|
64
|
+
vm.stopBroadcast();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ============================================================
|
|
68
|
+
// Factory (CREATE2) deployment
|
|
69
|
+
// ============================================================
|
|
70
|
+
|
|
71
|
+
/// @notice Deploy contract via CREATE2 factory with deterministic address
|
|
72
|
+
function deployByFactory(
|
|
73
|
+
string memory salt,
|
|
74
|
+
bytes memory creationCode,
|
|
75
|
+
bytes memory constructorArgs
|
|
76
|
+
) internal returns (address addr) {
|
|
77
|
+
require(address(FACTORY).code.length > 0, "factory not deployed on this chain");
|
|
78
|
+
bytes32 saltHash = keccak256(bytes(salt));
|
|
79
|
+
addr = FACTORY.getAddress(saltHash);
|
|
80
|
+
if (addr.code.length > 0) revert("already deployed");
|
|
81
|
+
bytes memory code = abi.encodePacked(creationCode, constructorArgs);
|
|
82
|
+
FACTORY.deploy(saltHash, code, 0);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/// @notice Deploy implementation + proxy via CREATE2 factory
|
|
86
|
+
function deployProxyByFactory(
|
|
87
|
+
string memory salt,
|
|
88
|
+
bytes memory implCreationCode,
|
|
89
|
+
bytes memory initData
|
|
90
|
+
) internal returns (address proxy, address impl) {
|
|
91
|
+
impl = deployDirect(implCreationCode, bytes(""));
|
|
92
|
+
proxy = deployByFactory(salt, type(ERC1967Proxy).creationCode, abi.encode(impl, initData));
|
|
93
|
+
console.log("Proxy:", proxy);
|
|
94
|
+
console.log("Implementation:", impl);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/// @notice Check if the CREATE2 factory is available on this chain
|
|
98
|
+
function isFactoryAvailable() internal view returns (bool) {
|
|
99
|
+
return address(FACTORY).code.length > 0;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/// @notice Get the predicted address for a given salt
|
|
103
|
+
function getFactoryAddress(string memory salt) internal view returns (address) {
|
|
104
|
+
require(isFactoryAvailable(), "factory not deployed on this chain");
|
|
105
|
+
return FACTORY.getAddress(keccak256(bytes(salt)));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/// @notice Check if a contract is already deployed at the factory address
|
|
109
|
+
function isFactoryDeployed(string memory salt) internal view returns (bool) {
|
|
110
|
+
if (!isFactoryAvailable()) return false;
|
|
111
|
+
return FACTORY.getAddress(keccak256(bytes(salt))).code.length > 0;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ============================================================
|
|
115
|
+
// Direct deployment
|
|
116
|
+
// ============================================================
|
|
117
|
+
|
|
118
|
+
/// @notice Deploy a new contract directly (non-deterministic address)
|
|
119
|
+
function deployDirect(
|
|
120
|
+
bytes memory creationCode,
|
|
121
|
+
bytes memory constructorArgs
|
|
122
|
+
) internal returns (address addr) {
|
|
123
|
+
bytes memory code = abi.encodePacked(creationCode, constructorArgs);
|
|
124
|
+
assembly {
|
|
125
|
+
addr := create(0, add(code, 0x20), mload(code))
|
|
126
|
+
}
|
|
127
|
+
require(addr != address(0), "deploy failed");
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/// @notice Deploy implementation + proxy directly
|
|
131
|
+
function deployProxy(
|
|
132
|
+
bytes memory implCreationCode,
|
|
133
|
+
bytes memory initData
|
|
134
|
+
) internal returns (address proxy, address impl) {
|
|
135
|
+
impl = deployDirect(implCreationCode, bytes(""));
|
|
136
|
+
ERC1967Proxy p = new ERC1967Proxy(impl, initData);
|
|
137
|
+
proxy = address(p);
|
|
138
|
+
console.log("Proxy:", proxy);
|
|
139
|
+
console.log("Implementation:", impl);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// ============================================================
|
|
143
|
+
// Upgrade
|
|
144
|
+
// ============================================================
|
|
145
|
+
|
|
146
|
+
/// @notice Upgrade a UUPS proxy to a new implementation
|
|
147
|
+
function upgradeProxy(address proxy, address newImpl) internal {
|
|
148
|
+
address oldImpl = _getImplementation(proxy);
|
|
149
|
+
(bool success,) = proxy.call(
|
|
150
|
+
abi.encodeWithSignature("upgradeToAndCall(address,bytes)", newImpl, bytes(""))
|
|
151
|
+
);
|
|
152
|
+
require(success, "upgrade failed");
|
|
153
|
+
console.log("Upgraded:", oldImpl, "->", newImpl);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/// @notice Deploy new implementation and upgrade proxy in one step
|
|
157
|
+
function deployAndUpgrade(address proxy, bytes memory implCreationCode) internal returns (address newImpl) {
|
|
158
|
+
newImpl = deployDirect(implCreationCode, bytes(""));
|
|
159
|
+
upgradeProxy(proxy, newImpl);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// ============================================================
|
|
163
|
+
// Deploy record (deploy.json) read/write
|
|
164
|
+
// ============================================================
|
|
165
|
+
|
|
166
|
+
/// @notice Read a deployed address from deployments/deploy.json
|
|
167
|
+
function readDeployment(string memory key) internal view returns (address) {
|
|
168
|
+
(string memory env, string memory chain) = _resolveDeploymentPath();
|
|
169
|
+
return _readDeploymentByPath(env, chain, key);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/// @notice Read a deployed address for a specific env and chain
|
|
173
|
+
function _readDeploymentByPath(string memory env, string memory chain, string memory key) internal view returns (address addr) {
|
|
174
|
+
string memory filePath = "deployments/deploy.json";
|
|
175
|
+
if (!vm.exists(filePath)) {
|
|
176
|
+
revert(string(abi.encodePacked("deploy.json not found: ", filePath)));
|
|
177
|
+
}
|
|
178
|
+
string memory json = vm.readFile(filePath);
|
|
179
|
+
// JSON path: .prod.Bsc.Gateway
|
|
180
|
+
string memory jsonPath = string(abi.encodePacked(".", env, ".", chain, ".", key));
|
|
181
|
+
addr = json.readAddress(jsonPath);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/// @notice Save a deployed address to deployments/deploy.json
|
|
185
|
+
function saveDeployment(string memory key, address addr) internal {
|
|
186
|
+
(string memory env, string memory chain) = _resolveDeploymentPath();
|
|
187
|
+
string memory filePath = "deployments/deploy.json";
|
|
188
|
+
// JSON path: .prod.Bsc.Gateway
|
|
189
|
+
string memory jsonPath = string(abi.encodePacked(".", env, ".", chain, ".", key));
|
|
190
|
+
string memory json = vm.readFile(filePath);
|
|
191
|
+
bool exists = vm.keyExistsJson(json, jsonPath);
|
|
192
|
+
if (!exists) {
|
|
193
|
+
revert(string(abi.encodePacked("key not found: ", env, ".", chain, ".", key)));
|
|
194
|
+
}
|
|
195
|
+
vm.writeJson(vm.toString(addr), filePath, jsonPath);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// ============================================================
|
|
199
|
+
// Internal helpers
|
|
200
|
+
// ============================================================
|
|
201
|
+
|
|
202
|
+
/// @notice Resolve deployment path (env, chain) based on chainId and NETWORK_ENV
|
|
203
|
+
/// @return env "prod", "main", or "test"
|
|
204
|
+
/// @return chain "Mapo", "Bsc", etc.
|
|
205
|
+
function _resolveDeploymentPath() internal view returns (string memory env, string memory chain) {
|
|
206
|
+
uint256 chainId = block.chainid;
|
|
207
|
+
env = vm.envString("NETWORK_ENV"); // required: test/prod/main
|
|
208
|
+
|
|
209
|
+
if (chainId == 212 || chainId == 22776) chain = "Mapo";
|
|
210
|
+
else if (chainId == 11155111 || chainId == 1) chain = "Eth";
|
|
211
|
+
else if (chainId == 97 || chainId == 56) chain = "Bsc";
|
|
212
|
+
else if (chainId == 8453) chain = "Base";
|
|
213
|
+
else if (chainId == 42161) chain = "Arb";
|
|
214
|
+
else if (chainId == 10) chain = "Op";
|
|
215
|
+
else if (chainId == 130) chain = "Uni";
|
|
216
|
+
else if (chainId == 137) chain = "Pol";
|
|
217
|
+
else if (chainId == 196) chain = "Xlayer";
|
|
218
|
+
else revert("unknown chain");
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/// @notice Read current implementation address from a UUPS proxy
|
|
222
|
+
function _getImplementation(address proxy) internal view returns (address) {
|
|
223
|
+
(bool success, bytes memory data) = proxy.staticcall(
|
|
224
|
+
abi.encodeWithSignature("getImplementation()")
|
|
225
|
+
);
|
|
226
|
+
if (success && data.length == 32) {
|
|
227
|
+
return abi.decode(data, (address));
|
|
228
|
+
}
|
|
229
|
+
return address(0);
|
|
230
|
+
}
|
|
231
|
+
}
|
package/utils/deployer.ts
CHANGED
|
@@ -13,12 +13,17 @@ import { verify as _verify } from "./verifier";
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
export interface DeployerOptions {
|
|
16
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Auto-verify contracts after deploy/upgrade.
|
|
18
|
+
* Failures are logged as warnings, never thrown — call deployer.verify() for explicit error handling.
|
|
19
|
+
* Defaults to false.
|
|
20
|
+
*/
|
|
21
|
+
autoVerify?: boolean;
|
|
17
22
|
}
|
|
18
23
|
|
|
19
24
|
export interface DeployResult {
|
|
20
|
-
address: string; //
|
|
21
|
-
hex?: string; // 0x hex
|
|
25
|
+
address: string; // EVM: 0x hex address. Tron: base58 (T...) address.
|
|
26
|
+
hex?: string; // Tron only: 0x hex address. Undefined on EVM.
|
|
22
27
|
}
|
|
23
28
|
|
|
24
29
|
export interface DeployProxyResult {
|
|
@@ -29,9 +34,16 @@ export interface DeployProxyResult {
|
|
|
29
34
|
}
|
|
30
35
|
|
|
31
36
|
export interface Deployer {
|
|
37
|
+
/**
|
|
38
|
+
* Deploy a contract.
|
|
39
|
+
* @param contractName - artifact name
|
|
40
|
+
* @param args - constructor arguments as raw values
|
|
41
|
+
* @param salt - "" or omitted = direct deploy; non-empty string = CREATE2 factory (deterministic address)
|
|
42
|
+
*/
|
|
32
43
|
deploy(contractName: string, args?: any[], salt?: string): Promise<DeployResult>;
|
|
33
44
|
deployProxy(contractName: string, initArgs?: any[], salt?: string): Promise<DeployProxyResult>;
|
|
34
45
|
upgrade(contractName: string, proxyAddr: string): Promise<DeployResult>;
|
|
46
|
+
/** Verify explicitly (throws on failure, unlike autoVerify which only warns). */
|
|
35
47
|
verify(contractName: string, address: string, constructorArgs?: any[], contractPath?: string): Promise<void>;
|
|
36
48
|
isTron: boolean;
|
|
37
49
|
network: string;
|
package/utils/dist/deployer.d.ts
CHANGED
|
@@ -8,6 +8,11 @@
|
|
|
8
8
|
* let newImpl = await deployer.upgrade("Gateway", proxyAddr);
|
|
9
9
|
*/
|
|
10
10
|
export interface DeployerOptions {
|
|
11
|
+
/**
|
|
12
|
+
* Auto-verify contracts after deploy/upgrade.
|
|
13
|
+
* Failures are logged as warnings, never thrown — call deployer.verify() for explicit error handling.
|
|
14
|
+
* Defaults to false.
|
|
15
|
+
*/
|
|
11
16
|
autoVerify?: boolean;
|
|
12
17
|
}
|
|
13
18
|
export interface DeployResult {
|
|
@@ -21,9 +26,16 @@ export interface DeployProxyResult {
|
|
|
21
26
|
implementationHex?: string;
|
|
22
27
|
}
|
|
23
28
|
export interface Deployer {
|
|
29
|
+
/**
|
|
30
|
+
* Deploy a contract.
|
|
31
|
+
* @param contractName - artifact name
|
|
32
|
+
* @param args - constructor arguments as raw values
|
|
33
|
+
* @param salt - "" or omitted = direct deploy; non-empty string = CREATE2 factory (deterministic address)
|
|
34
|
+
*/
|
|
24
35
|
deploy(contractName: string, args?: any[], salt?: string): Promise<DeployResult>;
|
|
25
36
|
deployProxy(contractName: string, initArgs?: any[], salt?: string): Promise<DeployProxyResult>;
|
|
26
37
|
upgrade(contractName: string, proxyAddr: string): Promise<DeployResult>;
|
|
38
|
+
/** Verify explicitly (throws on failure, unlike autoVerify which only warns). */
|
|
27
39
|
verify(contractName: string, address: string, constructorArgs?: any[], contractPath?: string): Promise<void>;
|
|
28
40
|
isTron: boolean;
|
|
29
41
|
network: string;
|
package/utils/dist/verifier.d.ts
CHANGED
|
@@ -2,7 +2,17 @@ export interface VerifyOptions {
|
|
|
2
2
|
address: string;
|
|
3
3
|
contractName: string;
|
|
4
4
|
contractPath?: string;
|
|
5
|
+
/**
|
|
6
|
+
* Constructor arguments as raw values — auto-encoded via ethers `encodeDeploy`.
|
|
7
|
+
* Example: ["0xAbC123...", "0xDeF456...", 200]
|
|
8
|
+
* Use this for most cases. Mutually exclusive with constructorParams.
|
|
9
|
+
*/
|
|
5
10
|
constructorArgs?: any[];
|
|
11
|
+
/**
|
|
12
|
+
* Pre-encoded constructor params as hex string (without 0x prefix).
|
|
13
|
+
* Use this when you already have the ABI-encoded bytes, e.g. from `cast abi-encode`.
|
|
14
|
+
* Takes priority over constructorArgs if both are provided.
|
|
15
|
+
*/
|
|
6
16
|
constructorParams?: string;
|
|
7
17
|
compiler?: string;
|
|
8
18
|
optimizer?: boolean;
|
package/utils/dist/verifier.js
CHANGED
|
@@ -27,7 +27,17 @@ async function verify(hre, opts) {
|
|
|
27
27
|
// EVM verification via hardhat-verify
|
|
28
28
|
// ============================================================
|
|
29
29
|
async function verifyEvm(hre, opts) {
|
|
30
|
-
|
|
30
|
+
// Resolve contractPath from hardhat artifact (authoritative) instead of guessing
|
|
31
|
+
let contractPath = opts.contractPath;
|
|
32
|
+
if (!contractPath) {
|
|
33
|
+
try {
|
|
34
|
+
const artifact = await hre.artifacts.readArtifact(opts.contractName);
|
|
35
|
+
contractPath = `${artifact.sourceName}:${opts.contractName}`;
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
contractPath = `contracts/${opts.contractName}.sol:${opts.contractName}`;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
31
41
|
console.log(`verifying ${opts.contractName} at ${opts.address} ...`);
|
|
32
42
|
try {
|
|
33
43
|
await hre.run("verify:verify", {
|
|
@@ -56,13 +66,13 @@ async function verifyTron(hre, opts) {
|
|
|
56
66
|
const chainId = hre.network.config.chainId;
|
|
57
67
|
const fs = require("fs");
|
|
58
68
|
const path = require("path");
|
|
59
|
-
//
|
|
69
|
+
// Compiler settings: opts > build-info > hardhat config (no hardcoded defaults)
|
|
60
70
|
const solcConfig = hre.config?.solidity?.compilers?.[0] || hre.config?.solidity || {};
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
71
|
+
let compiler = opts.compiler || solcConfig.version || "";
|
|
72
|
+
let optimizer = opts.optimizer ?? solcConfig.settings?.optimizer?.enabled;
|
|
73
|
+
let optimizerRuns = opts.optimizerRuns ?? solcConfig.settings?.optimizer?.runs;
|
|
74
|
+
let evmVersion = solcConfig.settings?.evmVersion || "";
|
|
75
|
+
let viaIR = solcConfig.settings?.viaIR ? "1" : "0";
|
|
66
76
|
// Convert address to Tron format
|
|
67
77
|
let address = opts.address;
|
|
68
78
|
if (address.startsWith("0x")) {
|
|
@@ -71,25 +81,26 @@ async function verifyTron(hre, opts) {
|
|
|
71
81
|
// Generate flattened source
|
|
72
82
|
const outputDir = path.join(process.cwd(), "verify-output");
|
|
73
83
|
const flattenPath = path.join(outputDir, `${opts.contractName}_flatten.sol`);
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
// Find actual source path from artifact
|
|
77
|
-
const artifact = await hre.artifacts.readArtifact(opts.contractName);
|
|
78
|
-
const sourcePath = artifact.sourceName; // e.g. "contracts/factory/Create2Factory.sol"
|
|
79
|
-
let flattenedSource = await hre.run("flatten:get-flattened-sources", {
|
|
80
|
-
files: [sourcePath],
|
|
81
|
-
});
|
|
82
|
-
flattenedSource = removeDuplicateSPDX(flattenedSource);
|
|
83
|
-
fs.mkdirSync(outputDir, { recursive: true });
|
|
84
|
-
fs.writeFileSync(flattenPath, flattenedSource);
|
|
85
|
-
console.log(`flatten saved to: ${flattenPath}`);
|
|
84
|
+
if (fs.existsSync(flattenPath)) {
|
|
85
|
+
console.log(`using existing flatten: ${flattenPath}`);
|
|
86
86
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
87
|
+
else {
|
|
88
|
+
console.log(`generating flatten for ${opts.contractName}...`);
|
|
89
|
+
let sourcePath = "";
|
|
90
|
+
try {
|
|
91
|
+
const artifact = await hre.artifacts.readArtifact(opts.contractName);
|
|
92
|
+
sourcePath = artifact.sourceName;
|
|
93
|
+
let flattenedSource = await hre.run("flatten:get-flattened-sources", {
|
|
94
|
+
files: [sourcePath],
|
|
95
|
+
});
|
|
96
|
+
flattenedSource = removeDuplicateSPDX(flattenedSource);
|
|
97
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
98
|
+
fs.writeFileSync(flattenPath, flattenedSource);
|
|
99
|
+
console.log(`flatten saved to: ${flattenPath}`);
|
|
90
100
|
}
|
|
91
|
-
|
|
92
|
-
|
|
101
|
+
catch (e) {
|
|
102
|
+
const hint = sourcePath || `contracts/${opts.contractName}.sol`;
|
|
103
|
+
console.log(`flatten failed, generate manually: npx hardhat flatten ${hint} > ${flattenPath}`);
|
|
93
104
|
return;
|
|
94
105
|
}
|
|
95
106
|
}
|
|
@@ -106,27 +117,49 @@ async function verifyTron(hre, opts) {
|
|
|
106
117
|
const form = new FormData();
|
|
107
118
|
form.append("contractAddress", address);
|
|
108
119
|
form.append("contractName", opts.contractName);
|
|
109
|
-
// Read
|
|
120
|
+
// Read compiler settings from build-info that contains this contract
|
|
110
121
|
let fullCompiler = `v${compiler}`;
|
|
111
122
|
try {
|
|
112
123
|
const buildInfoDir = path.join(process.cwd(), "artifacts/build-info");
|
|
113
|
-
const buildInfoFiles = fs.readdirSync(buildInfoDir);
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
124
|
+
const buildInfoFiles = fs.readdirSync(buildInfoDir).filter((f) => f.endsWith(".json"));
|
|
125
|
+
const artifact = await hre.artifacts.readArtifact(opts.contractName);
|
|
126
|
+
const sourceName = artifact.sourceName;
|
|
127
|
+
for (const file of buildInfoFiles) {
|
|
128
|
+
const buildInfo = JSON.parse(fs.readFileSync(path.join(buildInfoDir, file), "utf-8"));
|
|
129
|
+
if (buildInfo.output?.contracts?.[sourceName]?.[opts.contractName]) {
|
|
130
|
+
if (buildInfo.solcLongVersion) {
|
|
131
|
+
fullCompiler = `v${buildInfo.solcLongVersion}`;
|
|
132
|
+
}
|
|
133
|
+
// Override settings from actual build-info input
|
|
134
|
+
const settings = buildInfo.input?.settings;
|
|
135
|
+
if (settings) {
|
|
136
|
+
if (settings.evmVersion)
|
|
137
|
+
evmVersion = settings.evmVersion;
|
|
138
|
+
if (settings.optimizer != null) {
|
|
139
|
+
optimizer = settings.optimizer.enabled ?? optimizer;
|
|
140
|
+
optimizerRuns = settings.optimizer.runs ?? optimizerRuns;
|
|
141
|
+
}
|
|
142
|
+
if (settings.viaIR != null)
|
|
143
|
+
viaIR = settings.viaIR ? "1" : "0";
|
|
144
|
+
}
|
|
145
|
+
break;
|
|
118
146
|
}
|
|
119
147
|
}
|
|
120
148
|
}
|
|
121
149
|
catch (e) {
|
|
122
150
|
console.log(`[warn] could not read build-info: ${e.message}`);
|
|
123
151
|
}
|
|
152
|
+
if (!fullCompiler.includes("+commit.")) {
|
|
153
|
+
console.log(`[error] could not determine full compiler version (got: ${fullCompiler}). Run 'npx hardhat compile' to generate build-info.`);
|
|
154
|
+
printTronVerifyInfo(address, opts.contractName, fullCompiler, !!optimizer, optimizerRuns || 200, evmVersion || "london", flattenPath, chainId);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
124
157
|
form.append("compiler", fullCompiler);
|
|
125
158
|
form.append("license", "3"); // MIT
|
|
126
159
|
form.append("optimizer", optimizer ? "1" : "0");
|
|
127
|
-
form.append("runs", String(optimizerRuns));
|
|
160
|
+
form.append("runs", String(optimizerRuns ?? 200));
|
|
128
161
|
form.append("viaIR", viaIR);
|
|
129
|
-
form.append("evmVersion", evmVersion);
|
|
162
|
+
form.append("evmVersion", evmVersion || "london");
|
|
130
163
|
// Encode constructor params from ABI if not pre-encoded
|
|
131
164
|
let constructorParams = opts.constructorParams || "";
|
|
132
165
|
if (!constructorParams && opts.constructorArgs && opts.constructorArgs.length > 0) {
|
|
@@ -164,11 +197,14 @@ async function verifyTron(hre, opts) {
|
|
|
164
197
|
req.on("error", reject);
|
|
165
198
|
form.pipe(req);
|
|
166
199
|
});
|
|
167
|
-
|
|
168
|
-
|
|
200
|
+
console.log(`TronScan raw response:`, JSON.stringify(result, null, 2));
|
|
201
|
+
const status = result.data?.status;
|
|
202
|
+
if ((result.code === 200 && (status === 200 || status === 2006)) || result.success) {
|
|
203
|
+
console.log(`${opts.contractName} verified on TronScan`);
|
|
169
204
|
}
|
|
170
205
|
else {
|
|
171
|
-
console.log(`
|
|
206
|
+
console.log(`verification failed (status: ${status}): ${result.data?.message || "unknown"}`);
|
|
207
|
+
console.log(` compiler: ${fullCompiler}, evmVersion: ${evmVersion}, optimizer: ${optimizer}, runs: ${optimizerRuns}, viaIR: ${viaIR}`);
|
|
172
208
|
printTronVerifyInfo(address, opts.contractName, fullCompiler, optimizer, optimizerRuns, evmVersion, flattenPath, chainId);
|
|
173
209
|
}
|
|
174
210
|
}
|
package/utils/verifier.ts
CHANGED
|
@@ -11,14 +11,24 @@ const TRONSCAN_API: Record<number, string> = {
|
|
|
11
11
|
};
|
|
12
12
|
|
|
13
13
|
export interface VerifyOptions {
|
|
14
|
-
address: string; // contract address
|
|
14
|
+
address: string; // contract address (0x hex or Tron base58)
|
|
15
15
|
contractName: string; // e.g. "AuthorityManager"
|
|
16
16
|
contractPath?: string; // e.g. "contracts/AuthorityManager.sol:AuthorityManager"
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Constructor arguments as raw values — auto-encoded via ethers `encodeDeploy`.
|
|
19
|
+
* Example: ["0xAbC123...", "0xDeF456...", 200]
|
|
20
|
+
* Use this for most cases. Mutually exclusive with constructorParams.
|
|
21
|
+
*/
|
|
22
|
+
constructorArgs?: any[];
|
|
23
|
+
/**
|
|
24
|
+
* Pre-encoded constructor params as hex string (without 0x prefix).
|
|
25
|
+
* Use this when you already have the ABI-encoded bytes, e.g. from `cast abi-encode`.
|
|
26
|
+
* Takes priority over constructorArgs if both are provided.
|
|
27
|
+
*/
|
|
28
|
+
constructorParams?: string;
|
|
29
|
+
compiler?: string; // override solc version (auto-read from build-info if omitted)
|
|
30
|
+
optimizer?: boolean; // override optimizer enabled (auto-read from build-info if omitted)
|
|
31
|
+
optimizerRuns?: number; // override optimizer runs (auto-read from build-info if omitted)
|
|
22
32
|
}
|
|
23
33
|
|
|
24
34
|
/**
|
|
@@ -39,7 +49,16 @@ export async function verify(hre: any, opts: VerifyOptions): Promise<void> {
|
|
|
39
49
|
// ============================================================
|
|
40
50
|
|
|
41
51
|
async function verifyEvm(hre: any, opts: VerifyOptions): Promise<void> {
|
|
42
|
-
|
|
52
|
+
// Resolve contractPath from hardhat artifact (authoritative) instead of guessing
|
|
53
|
+
let contractPath = opts.contractPath;
|
|
54
|
+
if (!contractPath) {
|
|
55
|
+
try {
|
|
56
|
+
const artifact = await hre.artifacts.readArtifact(opts.contractName);
|
|
57
|
+
contractPath = `${artifact.sourceName}:${opts.contractName}`;
|
|
58
|
+
} catch {
|
|
59
|
+
contractPath = `contracts/${opts.contractName}.sol:${opts.contractName}`;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
43
62
|
|
|
44
63
|
console.log(`verifying ${opts.contractName} at ${opts.address} ...`);
|
|
45
64
|
|
|
@@ -71,13 +90,13 @@ async function verifyTron(hre: any, opts: VerifyOptions): Promise<void> {
|
|
|
71
90
|
const fs = require("fs");
|
|
72
91
|
const path = require("path");
|
|
73
92
|
|
|
74
|
-
//
|
|
93
|
+
// Compiler settings: opts > build-info > hardhat config (no hardcoded defaults)
|
|
75
94
|
const solcConfig = hre.config?.solidity?.compilers?.[0] || hre.config?.solidity || {};
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
95
|
+
let compiler = opts.compiler || solcConfig.version || "";
|
|
96
|
+
let optimizer = opts.optimizer ?? solcConfig.settings?.optimizer?.enabled;
|
|
97
|
+
let optimizerRuns = opts.optimizerRuns ?? solcConfig.settings?.optimizer?.runs;
|
|
98
|
+
let evmVersion = solcConfig.settings?.evmVersion || "";
|
|
99
|
+
let viaIR = solcConfig.settings?.viaIR ? "1" : "0";
|
|
81
100
|
|
|
82
101
|
// Convert address to Tron format
|
|
83
102
|
let address = opts.address;
|
|
@@ -89,23 +108,24 @@ async function verifyTron(hre: any, opts: VerifyOptions): Promise<void> {
|
|
|
89
108
|
const outputDir = path.join(process.cwd(), "verify-output");
|
|
90
109
|
const flattenPath = path.join(outputDir, `${opts.contractName}_flatten.sol`);
|
|
91
110
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
console.log(`
|
|
107
|
-
}
|
|
108
|
-
|
|
111
|
+
if (fs.existsSync(flattenPath)) {
|
|
112
|
+
console.log(`using existing flatten: ${flattenPath}`);
|
|
113
|
+
} else {
|
|
114
|
+
console.log(`generating flatten for ${opts.contractName}...`);
|
|
115
|
+
let sourcePath = "";
|
|
116
|
+
try {
|
|
117
|
+
const artifact = await hre.artifacts.readArtifact(opts.contractName);
|
|
118
|
+
sourcePath = artifact.sourceName;
|
|
119
|
+
let flattenedSource = await hre.run("flatten:get-flattened-sources", {
|
|
120
|
+
files: [sourcePath],
|
|
121
|
+
});
|
|
122
|
+
flattenedSource = removeDuplicateSPDX(flattenedSource);
|
|
123
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
124
|
+
fs.writeFileSync(flattenPath, flattenedSource);
|
|
125
|
+
console.log(`flatten saved to: ${flattenPath}`);
|
|
126
|
+
} catch (e) {
|
|
127
|
+
const hint = sourcePath || `contracts/${opts.contractName}.sol`;
|
|
128
|
+
console.log(`flatten failed, generate manually: npx hardhat flatten ${hint} > ${flattenPath}`);
|
|
109
129
|
return;
|
|
110
130
|
}
|
|
111
131
|
}
|
|
@@ -124,28 +144,47 @@ async function verifyTron(hre: any, opts: VerifyOptions): Promise<void> {
|
|
|
124
144
|
const form = new FormData();
|
|
125
145
|
form.append("contractAddress", address);
|
|
126
146
|
form.append("contractName", opts.contractName);
|
|
127
|
-
// Read
|
|
147
|
+
// Read compiler settings from build-info that contains this contract
|
|
128
148
|
let fullCompiler = `v${compiler}`;
|
|
129
149
|
try {
|
|
130
150
|
const buildInfoDir = path.join(process.cwd(), "artifacts/build-info");
|
|
131
|
-
const buildInfoFiles = fs.readdirSync(buildInfoDir);
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
151
|
+
const buildInfoFiles = fs.readdirSync(buildInfoDir).filter((f: string) => f.endsWith(".json"));
|
|
152
|
+
const artifact = await hre.artifacts.readArtifact(opts.contractName);
|
|
153
|
+
const sourceName = artifact.sourceName;
|
|
154
|
+
|
|
155
|
+
for (const file of buildInfoFiles) {
|
|
156
|
+
const buildInfo = JSON.parse(fs.readFileSync(path.join(buildInfoDir, file), "utf-8"));
|
|
157
|
+
if (buildInfo.output?.contracts?.[sourceName]?.[opts.contractName]) {
|
|
158
|
+
if (buildInfo.solcLongVersion) {
|
|
159
|
+
fullCompiler = `v${buildInfo.solcLongVersion}`;
|
|
160
|
+
}
|
|
161
|
+
// Override settings from actual build-info input
|
|
162
|
+
const settings = buildInfo.input?.settings;
|
|
163
|
+
if (settings) {
|
|
164
|
+
if (settings.evmVersion) evmVersion = settings.evmVersion;
|
|
165
|
+
if (settings.optimizer != null) {
|
|
166
|
+
optimizer = settings.optimizer.enabled ?? optimizer;
|
|
167
|
+
optimizerRuns = settings.optimizer.runs ?? optimizerRuns;
|
|
168
|
+
}
|
|
169
|
+
if (settings.viaIR != null) viaIR = settings.viaIR ? "1" : "0";
|
|
170
|
+
}
|
|
171
|
+
break;
|
|
138
172
|
}
|
|
139
173
|
}
|
|
140
174
|
} catch (e: any) {
|
|
141
175
|
console.log(`[warn] could not read build-info: ${e.message}`);
|
|
142
176
|
}
|
|
177
|
+
if (!fullCompiler.includes("+commit.")) {
|
|
178
|
+
console.log(`[error] could not determine full compiler version (got: ${fullCompiler}). Run 'npx hardhat compile' to generate build-info.`);
|
|
179
|
+
printTronVerifyInfo(address, opts.contractName, fullCompiler, !!optimizer, optimizerRuns || 200, evmVersion || "london", flattenPath, chainId);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
143
182
|
form.append("compiler", fullCompiler);
|
|
144
183
|
form.append("license", "3"); // MIT
|
|
145
184
|
form.append("optimizer", optimizer ? "1" : "0");
|
|
146
|
-
form.append("runs", String(optimizerRuns));
|
|
185
|
+
form.append("runs", String(optimizerRuns ?? 200));
|
|
147
186
|
form.append("viaIR", viaIR);
|
|
148
|
-
form.append("evmVersion", evmVersion);
|
|
187
|
+
form.append("evmVersion", evmVersion || "london");
|
|
149
188
|
// Encode constructor params from ABI if not pre-encoded
|
|
150
189
|
let constructorParams = opts.constructorParams || "";
|
|
151
190
|
if (!constructorParams && opts.constructorArgs && opts.constructorArgs.length > 0) {
|
|
@@ -180,10 +219,13 @@ async function verifyTron(hre: any, opts: VerifyOptions): Promise<void> {
|
|
|
180
219
|
form.pipe(req);
|
|
181
220
|
});
|
|
182
221
|
|
|
183
|
-
|
|
184
|
-
|
|
222
|
+
console.log(`TronScan raw response:`, JSON.stringify(result, null, 2));
|
|
223
|
+
const status = result.data?.status;
|
|
224
|
+
if ((result.code === 200 && (status === 200 || status === 2006)) || result.success) {
|
|
225
|
+
console.log(`${opts.contractName} verified on TronScan`);
|
|
185
226
|
} else {
|
|
186
|
-
console.log(`
|
|
227
|
+
console.log(`verification failed (status: ${status}): ${result.data?.message || "unknown"}`);
|
|
228
|
+
console.log(` compiler: ${fullCompiler}, evmVersion: ${evmVersion}, optimizer: ${optimizer}, runs: ${optimizerRuns}, viaIR: ${viaIR}`);
|
|
187
229
|
printTronVerifyInfo(address, opts.contractName, fullCompiler, optimizer, optimizerRuns, evmVersion, flattenPath, chainId);
|
|
188
230
|
}
|
|
189
231
|
} catch (e: any) {
|