-
Notifications
You must be signed in to change notification settings - Fork 126
/
index.ts
161 lines (143 loc) · 4.86 KB
/
index.ts
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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
import { program } from 'commander'
import { Wallet } from '@ethersproject/wallet'
import { JsonRpcProvider, TransactionReceipt } from '@ethersproject/providers'
import { AddressZero } from '@ethersproject/constants'
import { getAddress } from '@ethersproject/address'
import fs from 'fs'
import deploy from './src/deploy'
import { MigrationState } from './src/migrations'
import { asciiStringToBytes32 } from './src/util/asciiStringToBytes32'
import { version } from './package.json'
program
.requiredOption('-pk, --private-key <string>', 'Private key used to deploy all contracts')
.requiredOption('-j, --json-rpc <url>', 'JSON RPC URL where the program should be deployed')
.requiredOption('-w9, --weth9-address <address>', 'Address of the WETH9 contract on this chain')
.requiredOption('-ncl, --native-currency-label <string>', 'Native currency label, e.g. ETH')
.requiredOption(
'-o, --owner-address <address>',
'Contract address that will own the deployed artifacts after the script runs'
)
.option('-s, --state <path>', 'Path to the JSON file containing the migrations state (optional)', './state.json')
.option('-v2, --v2-core-factory-address <address>', 'The V2 core factory address used in the swap router (optional)')
.option('-g, --gas-price <number>', 'The gas price to pay in GWEI for each transaction (optional)')
.option('-c, --confirmations <number>', 'How many confirmations to wait for after each transaction (optional)', '2')
program.name('npx @uniswap/deploy-v3').version(version).parse(process.argv)
if (!/^0x[a-zA-Z0-9]{64}$/.test(program.privateKey)) {
console.error('Invalid private key!')
process.exit(1)
}
let url: URL
try {
url = new URL(program.jsonRpc)
} catch (error) {
console.error('Invalid JSON RPC URL', (error as Error).message)
process.exit(1)
}
let gasPrice: number | undefined
try {
gasPrice = program.gasPrice ? parseInt(program.gasPrice) : undefined
} catch (error) {
console.error('Failed to parse gas price', (error as Error).message)
process.exit(1)
}
let confirmations: number
try {
confirmations = parseInt(program.confirmations)
} catch (error) {
console.error('Failed to parse confirmations', (error as Error).message)
process.exit(1)
}
let nativeCurrencyLabelBytes: string
try {
nativeCurrencyLabelBytes = asciiStringToBytes32(program.nativeCurrencyLabel)
} catch (error) {
console.error('Invalid native currency label', (error as Error).message)
process.exit(1)
}
let weth9Address: string
try {
weth9Address = getAddress(program.weth9Address)
} catch (error) {
console.error('Invalid WETH9 address', (error as Error).message)
process.exit(1)
}
let v2CoreFactoryAddress: string
if (typeof program.v2CoreFactoryAddress === 'undefined') {
v2CoreFactoryAddress = AddressZero
} else {
try {
v2CoreFactoryAddress = getAddress(program.v2CoreFactoryAddress)
} catch (error) {
console.error('Invalid V2 factory address', (error as Error).message)
process.exit(1)
}
}
let ownerAddress: string
try {
ownerAddress = getAddress(program.ownerAddress)
} catch (error) {
console.error('Invalid owner address', (error as Error).message)
process.exit(1)
}
const wallet = new Wallet(program.privateKey, new JsonRpcProvider({ url: url.href }))
let state: MigrationState
if (fs.existsSync(program.state)) {
try {
state = JSON.parse(fs.readFileSync(program.state, { encoding: 'utf8' }))
} catch (error) {
console.error('Failed to load and parse migration state file', (error as Error).message)
process.exit(1)
}
} else {
state = {}
}
let finalState: MigrationState
const onStateChange = async (newState: MigrationState): Promise<void> => {
fs.writeFileSync(program.state, JSON.stringify(newState))
finalState = newState
}
async function run() {
let step = 1
const results = []
const generator = deploy({
signer: wallet,
gasPrice,
nativeCurrencyLabelBytes,
v2CoreFactoryAddress,
ownerAddress,
weth9Address,
initialState: state,
onStateChange,
})
for await (const result of generator) {
console.log(`Step ${step++} complete`, result)
results.push(result)
// wait 15 minutes for any transactions sent in the step
await Promise.all(
result.map(
(stepResult): Promise<TransactionReceipt | true> => {
if (stepResult.hash) {
return wallet.provider.waitForTransaction(stepResult.hash, confirmations, /* 15 minutes */ 1000 * 60 * 15)
} else {
return Promise.resolve(true)
}
}
)
)
}
return results
}
run()
.then((results) => {
console.log('Deployment succeeded')
console.log(JSON.stringify(results))
console.log('Final state')
console.log(JSON.stringify(finalState))
process.exit(0)
})
.catch((error) => {
console.error('Deployment failed', error)
console.log('Final state')
console.log(JSON.stringify(finalState))
process.exit(1)
})