@mapprotocol/common-contracts 0.3.1 → 0.4.0
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 +18 -3
- 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/addressCodec.d.ts +14 -0
- package/utils/dist/addressCodec.js +61 -0
- package/utils/dist/codeHash.d.ts +24 -0
- package/utils/dist/codeHash.js +60 -0
- package/utils/dist/deployRecord.d.ts +37 -0
- package/utils/dist/deployRecord.js +104 -0
- package/utils/dist/deployer.d.ts +48 -0
- package/utils/dist/deployer.js +80 -0
- package/utils/dist/evmHelper.d.ts +33 -0
- package/utils/dist/evmHelper.js +83 -0
- package/utils/dist/factory.d.ts +36 -0
- package/utils/dist/factory.js +187 -0
- package/utils/dist/index.d.ts +17 -0
- package/utils/dist/index.js +45 -0
- package/utils/dist/tronHelper.d.ts +127 -0
- package/utils/dist/tronHelper.js +291 -0
- package/utils/dist/verifier.d.ts +24 -0
- package/utils/dist/verifier.js +229 -0
- package/utils/verifier.ts +72 -41
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,15 +1,29 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mapprotocol/common-contracts",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
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",
|
|
10
22
|
"!artifacts/contracts/**/*.dbg.json",
|
|
11
23
|
"typechain-types/**/*",
|
|
12
24
|
"utils/**/*.ts",
|
|
25
|
+
"utils/dist/**/*",
|
|
26
|
+
"script/base/**/*.sol",
|
|
13
27
|
"README.md"
|
|
14
28
|
],
|
|
15
29
|
"repository": {
|
|
@@ -31,12 +45,13 @@
|
|
|
31
45
|
"test": "forge test",
|
|
32
46
|
"test:hardhat": "hardhat test",
|
|
33
47
|
"format": "forge fmt",
|
|
34
|
-
"
|
|
48
|
+
"build:utils": "cd utils && tsc",
|
|
49
|
+
"clean": "forge clean && hardhat clean && rm -rf typechain-types utils/dist",
|
|
35
50
|
"compile": "hardhat compile",
|
|
36
51
|
"typecheck": "tsc --noEmit",
|
|
37
52
|
"gas-report": "forge test --gas-report",
|
|
38
53
|
"coverage": "forge coverage",
|
|
39
|
-
"prepublishOnly": "npm run clean && npm run build:hardhat && npm run typecheck"
|
|
54
|
+
"prepublishOnly": "npm run clean && npm run build:hardhat && npm run build:utils && npm run typecheck"
|
|
40
55
|
},
|
|
41
56
|
"keywords": [
|
|
42
57
|
"solidity",
|
|
@@ -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;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert any address format to hex bytes.
|
|
3
|
+
* - EVM address (0x prefix): return as-is lowercase
|
|
4
|
+
* - Tron address (T prefix, 34 chars, 0x41 byte): convert via tronToHex
|
|
5
|
+
* - Solana/other base58 address: base58 decode to full bytes
|
|
6
|
+
* @param addr - address in any supported format
|
|
7
|
+
*/
|
|
8
|
+
export declare function addressToHex(addr: string): string;
|
|
9
|
+
/** Check if a string is valid base58 encoding. */
|
|
10
|
+
export declare function isBase58(addr: string): boolean;
|
|
11
|
+
/** Check if address is Tron format (T prefix, 34 chars, 0x41 first byte after decode). */
|
|
12
|
+
export declare function isTronAddress(addr: string): boolean;
|
|
13
|
+
/** Check if chain name is Solana. */
|
|
14
|
+
export declare function isSolanaChain(chainName: string): boolean;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.addressToHex = addressToHex;
|
|
7
|
+
exports.isBase58 = isBase58;
|
|
8
|
+
exports.isTronAddress = isTronAddress;
|
|
9
|
+
exports.isSolanaChain = isSolanaChain;
|
|
10
|
+
/**
|
|
11
|
+
* Multi-chain address encoding — converts EVM, Tron, and Solana addresses to hex bytes.
|
|
12
|
+
*/
|
|
13
|
+
const bs58_1 = __importDefault(require("bs58"));
|
|
14
|
+
const tronHelper_1 = require("./tronHelper");
|
|
15
|
+
/**
|
|
16
|
+
* Convert any address format to hex bytes.
|
|
17
|
+
* - EVM address (0x prefix): return as-is lowercase
|
|
18
|
+
* - Tron address (T prefix, 34 chars, 0x41 byte): convert via tronToHex
|
|
19
|
+
* - Solana/other base58 address: base58 decode to full bytes
|
|
20
|
+
* @param addr - address in any supported format
|
|
21
|
+
*/
|
|
22
|
+
function addressToHex(addr) {
|
|
23
|
+
if (addr.startsWith("0x")) {
|
|
24
|
+
return addr.toLowerCase();
|
|
25
|
+
}
|
|
26
|
+
if (isTronAddress(addr)) {
|
|
27
|
+
return (0, tronHelper_1.tronToHex)(addr);
|
|
28
|
+
}
|
|
29
|
+
if (isBase58(addr)) {
|
|
30
|
+
const decoded = bs58_1.default.decode(addr);
|
|
31
|
+
return "0x" + Buffer.from(decoded).toString("hex");
|
|
32
|
+
}
|
|
33
|
+
throw new Error(`Unknown address format: ${addr}`);
|
|
34
|
+
}
|
|
35
|
+
/** Check if a string is valid base58 encoding. */
|
|
36
|
+
function isBase58(addr) {
|
|
37
|
+
const base58Chars = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
38
|
+
for (const char of addr) {
|
|
39
|
+
if (!base58Chars.includes(char)) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
/** Check if address is Tron format (T prefix, 34 chars, 0x41 first byte after decode). */
|
|
46
|
+
function isTronAddress(addr) {
|
|
47
|
+
if (!addr.startsWith("T") || addr.length !== 34 || !isBase58(addr))
|
|
48
|
+
return false;
|
|
49
|
+
try {
|
|
50
|
+
const decoded = bs58_1.default.decode(addr);
|
|
51
|
+
// Tron: 0x41 (1 byte) + address (20 bytes) + checksum (4 bytes) = 25 bytes
|
|
52
|
+
return decoded.length === 25 && decoded[0] === 0x41;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/** Check if chain name is Solana. */
|
|
59
|
+
function isSolanaChain(chainName) {
|
|
60
|
+
return chainName === "Sol" || chainName === "sol_test";
|
|
61
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get codeHash for a contract deployment.
|
|
3
|
+
* @param artifacts - hardhat artifacts (hre.artifacts)
|
|
4
|
+
* @param contractName - contract name (e.g. "Gateway", "AuthorityManager")
|
|
5
|
+
* @param args - constructor arguments array
|
|
6
|
+
*/
|
|
7
|
+
export declare function getCodeHash(artifacts: any, contractName: string, args?: any[]): Promise<string>;
|
|
8
|
+
/**
|
|
9
|
+
* Get codeHash for an ERC1967Proxy deployment.
|
|
10
|
+
* @param artifacts - hardhat artifacts (hre.artifacts)
|
|
11
|
+
* @param implAddress - implementation address
|
|
12
|
+
* @param implName - implementation contract name (for encoding initData)
|
|
13
|
+
* @param initArgs - arguments for initialize() function
|
|
14
|
+
*/
|
|
15
|
+
export declare function getProxyCodeHash(artifacts: any, implAddress: string, implName: string, initArgs?: any[]): Promise<string>;
|
|
16
|
+
/**
|
|
17
|
+
* Get codeHash for a custom proxy deployment.
|
|
18
|
+
* @param artifacts - hardhat artifacts (hre.artifacts)
|
|
19
|
+
* @param proxyName - proxy contract name (e.g. "MyCustomProxy")
|
|
20
|
+
* @param implAddress - implementation address
|
|
21
|
+
* @param implName - implementation contract name (for encoding initData)
|
|
22
|
+
* @param initArgs - arguments for initialize() function
|
|
23
|
+
*/
|
|
24
|
+
export declare function getCustomProxyCodeHash(artifacts: any, proxyName: string, implAddress: string, implName: string, initArgs?: any[]): Promise<string>;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getCodeHash = getCodeHash;
|
|
4
|
+
exports.getProxyCodeHash = getProxyCodeHash;
|
|
5
|
+
exports.getCustomProxyCodeHash = getCustomProxyCodeHash;
|
|
6
|
+
/**
|
|
7
|
+
* Calculate creationCode hash for CREATE2 address prediction.
|
|
8
|
+
*/
|
|
9
|
+
const { keccak256, Interface } = require("ethers");
|
|
10
|
+
/**
|
|
11
|
+
* Get codeHash for a contract deployment.
|
|
12
|
+
* @param artifacts - hardhat artifacts (hre.artifacts)
|
|
13
|
+
* @param contractName - contract name (e.g. "Gateway", "AuthorityManager")
|
|
14
|
+
* @param args - constructor arguments array
|
|
15
|
+
*/
|
|
16
|
+
async function getCodeHash(artifacts, contractName, args = []) {
|
|
17
|
+
const artifact = await artifacts.readArtifact(contractName);
|
|
18
|
+
let bytecode = artifact.bytecode;
|
|
19
|
+
if (!bytecode.startsWith("0x"))
|
|
20
|
+
bytecode = "0x" + bytecode;
|
|
21
|
+
if (args.length > 0) {
|
|
22
|
+
const iface = new Interface(artifact.abi);
|
|
23
|
+
const encoded = iface.encodeDeploy(args);
|
|
24
|
+
return keccak256(bytecode + encoded.slice(2));
|
|
25
|
+
}
|
|
26
|
+
return keccak256(bytecode);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Get codeHash for an ERC1967Proxy deployment.
|
|
30
|
+
* @param artifacts - hardhat artifacts (hre.artifacts)
|
|
31
|
+
* @param implAddress - implementation address
|
|
32
|
+
* @param implName - implementation contract name (for encoding initData)
|
|
33
|
+
* @param initArgs - arguments for initialize() function
|
|
34
|
+
*/
|
|
35
|
+
async function getProxyCodeHash(artifacts, implAddress, implName, initArgs = []) {
|
|
36
|
+
return getCustomProxyCodeHash(artifacts, "ERC1967Proxy", implAddress, implName, initArgs);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Get codeHash for a custom proxy deployment.
|
|
40
|
+
* @param artifacts - hardhat artifacts (hre.artifacts)
|
|
41
|
+
* @param proxyName - proxy contract name (e.g. "MyCustomProxy")
|
|
42
|
+
* @param implAddress - implementation address
|
|
43
|
+
* @param implName - implementation contract name (for encoding initData)
|
|
44
|
+
* @param initArgs - arguments for initialize() function
|
|
45
|
+
*/
|
|
46
|
+
async function getCustomProxyCodeHash(artifacts, proxyName, implAddress, implName, initArgs = []) {
|
|
47
|
+
const proxyArtifact = await artifacts.readArtifact(proxyName);
|
|
48
|
+
let bytecode = proxyArtifact.bytecode;
|
|
49
|
+
if (!bytecode.startsWith("0x"))
|
|
50
|
+
bytecode = "0x" + bytecode;
|
|
51
|
+
let initData = "0x";
|
|
52
|
+
if (initArgs.length > 0) {
|
|
53
|
+
const implArtifact = await artifacts.readArtifact(implName);
|
|
54
|
+
const iface = new Interface(implArtifact.abi);
|
|
55
|
+
initData = iface.encodeFunctionData("initialize", initArgs);
|
|
56
|
+
}
|
|
57
|
+
const { AbiCoder } = require("ethers");
|
|
58
|
+
const constructorArgs = AbiCoder.defaultAbiCoder().encode(["address", "bytes"], [implAddress, initData]);
|
|
59
|
+
return keccak256(bytecode + constructorArgs.slice(2));
|
|
60
|
+
}
|