@@ -3,21 +3,20 @@ import { join } from 'node:path';
3
3
4
4
import { encode } from '@api3/airnode-abi' ;
5
5
import {
6
- type Address ,
7
- type Hex ,
8
6
deriveBeaconId ,
9
7
interpolateSecretsIntoConfig ,
10
8
loadConfig ,
11
9
loadSecrets ,
10
+ type Address ,
11
+ type Hex ,
12
12
} from '@api3/commons' ;
13
13
import {
14
- AirseekerRegistry__factory as AirseekerRegistryFactory ,
15
14
AccessControlRegistry__factory as AccessControlRegistryFactory ,
15
+ AirseekerRegistry__factory as AirseekerRegistryFactory ,
16
16
Api3ServerV1__factory as Api3ServerV1Factory ,
17
17
} from '@api3/contracts' ;
18
18
import dotenv from 'dotenv' ;
19
- import type { ContractTransactionResponse , Signer } from 'ethers' ;
20
- import { ethers } from 'ethers' ;
19
+ import { NonceManager , ethers } from 'ethers' ;
21
20
import { zip } from 'lodash' ;
22
21
23
22
import {
@@ -59,22 +58,29 @@ export const deriveRole = (adminRole: string, roleDescription: string) => {
59
58
60
59
// NOTE: This function is not used by the initialization script, but you can use it after finishing Airseeker test on a
61
60
// public testnet to refund test ETH from sponsor wallets to the funder wallet.
62
- export const refundFunder = async ( funderWallet : ethers . HDNodeWallet ) => {
63
- const airseekerSecrets = dotenv . parse ( readFileSync ( join ( __dirname , `/../airseeker` , 'secrets.env' ) , 'utf8' ) ) ;
61
+ export const refundFunder = async ( funderWallet : ethers . NonceManager ) => {
62
+ const configPath = join ( __dirname , `/../airseeker` ) ;
63
+ const rawConfig = loadConfig ( join ( configPath , 'airseeker.json' ) ) ;
64
+ const airseekerSecrets = dotenv . parse ( readFileSync ( join ( configPath , 'secrets.env' ) , 'utf8' ) ) ;
64
65
const airseekerWalletMnemonic = airseekerSecrets . SPONSOR_WALLET_MNEMONIC ;
65
66
if ( ! airseekerWalletMnemonic ) throw new Error ( 'SPONSOR_WALLET_MNEMONIC not found in Airseeker secrets' ) ;
66
67
67
68
// Initialize sponsor wallets
68
- for ( const beaconSetName of getBeaconSetNames ( ) ) {
69
+ const beaconSetNames = await getBeaconSetNames ( ) ;
70
+ for ( const beaconSetName of beaconSetNames ) {
69
71
const dapiName = encodeDapiName ( beaconSetName ) ;
70
72
71
- const sponsorAddressHash = deriveSponsorAddressHashForManagedFeed ( dapiName ) ;
73
+ const sponsorAddressHash =
74
+ rawConfig . walletDerivationScheme . type === 'fallback'
75
+ ? rawConfig . walletDerivationScheme . sponsorAddress
76
+ : deriveSponsorAddressHashForManagedFeed ( dapiName ) ;
72
77
const sponsorWallet = deriveSponsorWalletFromSponsorAddressHash (
73
78
airseekerWalletMnemonic ,
74
79
sponsorAddressHash
75
80
) . connect ( funderWallet . provider ) ;
76
- const sponsorWalletBalance = await funderWallet . provider ! . getBalance ( sponsorWallet . address ) ;
77
- console . info ( 'Sponsor wallet balance:' , ethers . formatEther ( sponsorWalletBalance . toString ( ) ) ) ;
81
+ const sponsorWalletAddress = await sponsorWallet . getAddress ( ) ;
82
+ const sponsorWalletBalance = await funderWallet . provider ! . getBalance ( sponsorWalletAddress ) ;
83
+ console . info ( 'Sponsor wallet balance:' , sponsorWalletAddress , ethers . formatEther ( sponsorWalletBalance . toString ( ) ) ) ;
78
84
79
85
const feeData = await sponsorWallet . provider ! . getFeeData ( ) ;
80
86
const { gasPrice } = feeData ;
@@ -86,7 +92,7 @@ export const refundFunder = async (funderWallet: ethers.HDNodeWallet) => {
86
92
continue ;
87
93
}
88
94
const tx = await sponsorWallet . sendTransaction ( {
89
- to : funderWallet . address ,
95
+ to : await funderWallet . getAddress ( ) ,
90
96
gasPrice,
91
97
gasLimit : BigInt ( 21_000 ) ,
92
98
value : sponsorWalletBalance - gasFee ,
@@ -95,7 +101,7 @@ export const refundFunder = async (funderWallet: ethers.HDNodeWallet) => {
95
101
96
102
console . info ( `Refunding funder wallet from sponsor wallet` , {
97
103
dapiName,
98
- sponsorWalletAddress : sponsorWallet . address ,
104
+ sponsorWalletAddress,
99
105
} ) ;
100
106
}
101
107
} ;
@@ -112,64 +118,80 @@ const loadAirnodeFeedConfig = (airnodeFeedDir: 'airnode-feed-1' | 'airnode-feed-
112
118
return interpolateSecretsIntoConfig ( rawConfig , rawSecrets ) ;
113
119
} ;
114
120
115
- const getBeaconSetNames = ( ) => {
121
+ const getBeaconSetNames = async ( ) => {
116
122
const airnodeFeed = loadAirnodeFeedConfig ( 'airnode-feed-1' ) ;
117
123
const airnodeFeedWallet = ethers . Wallet . fromPhrase ( airnodeFeed . nodeSettings . airnodeWalletMnemonic ) ;
124
+ const airnodeFeedWalletAddress = await airnodeFeedWallet . getAddress ( ) ;
118
125
const airnodeFeedBeacons = Object . values ( airnodeFeed . templates ) . map ( ( template : any ) => {
119
- return deriveBeaconData ( { ...template , airnodeAddress : airnodeFeedWallet . address } ) ;
126
+ return deriveBeaconData ( { ...template , airnodeAddress : airnodeFeedWalletAddress } ) ;
120
127
} ) ;
121
128
122
129
return airnodeFeedBeacons . map ( ( beacon ) => beacon . parameters [ 0 ] ! . value ) ;
123
130
} ;
124
131
125
- export const fundAirseekerSponsorWallet = async ( funderWallet : ethers . HDNodeWallet ) => {
132
+ export const fundAirseekerSponsorWallet = async ( funderWallet : ethers . NonceManager ) => {
133
+ const configPath = join ( __dirname , `/../airseeker` ) ;
134
+ const rawConfig = loadConfig ( join ( configPath , 'airseeker.json' ) ) ;
126
135
const airseekerSecrets = dotenv . parse ( readFileSync ( join ( __dirname , `/../airseeker` , 'secrets.env' ) , 'utf8' ) ) ;
127
136
const airseekerWalletMnemonic = airseekerSecrets . SPONSOR_WALLET_MNEMONIC ;
128
137
if ( ! airseekerWalletMnemonic ) throw new Error ( 'SPONSOR_WALLET_MNEMONIC not found in Airseeker secrets' ) ;
129
138
130
139
// Initialize sponsor wallets
131
- for ( const beaconSetName of getBeaconSetNames ( ) ) {
140
+ const beaconSetNames = await getBeaconSetNames ( ) ;
141
+ for ( const beaconSetName of beaconSetNames ) {
132
142
const dapiName = encodeDapiName ( beaconSetName ) ;
133
143
134
- const sponsorAddressHash = deriveSponsorAddressHashForManagedFeed ( dapiName ) ;
144
+ const sponsorAddressHash =
145
+ rawConfig . walletDerivationScheme . type === 'fallback'
146
+ ? rawConfig . walletDerivationScheme . sponsorAddress
147
+ : deriveSponsorAddressHashForManagedFeed ( dapiName ) ;
135
148
const sponsorWallet = deriveSponsorWalletFromSponsorAddressHash ( airseekerWalletMnemonic , sponsorAddressHash ) ;
136
- const sponsorWalletBalance = await funderWallet . provider ! . getBalance ( sponsorWallet . address ) ;
149
+ const sponsorWalletAddress = await sponsorWallet . getAddress ( ) ;
150
+ const sponsorWalletBalance = await funderWallet . provider ! . getBalance ( sponsorWalletAddress ) ;
137
151
console . info ( 'Sponsor wallet balance:' , ethers . formatEther ( sponsorWalletBalance . toString ( ) ) ) ;
138
152
139
153
const tx = await funderWallet . sendTransaction ( {
140
- to : sponsorWallet . address ,
141
- value : ethers . parseEther ( '1' ) ,
154
+ to : sponsorWalletAddress ,
155
+ value : ethers . parseEther ( '0. 1' ) ,
142
156
} ) ;
143
157
await tx . wait ( ) ;
144
158
145
159
console . info ( `Funding sponsor wallets` , {
146
160
dapiName,
147
- sponsorWalletAddress : sponsorWallet . address ,
161
+ decodedDapiName : ethers . decodeBytes32String ( dapiName ) ,
162
+ sponsorWalletAddress,
148
163
} ) ;
149
164
}
150
165
} ;
151
166
152
- export const deploy = async ( funderWallet : ethers . HDNodeWallet , provider : ethers . JsonRpcProvider ) => {
167
+ export const deploy = async ( funderWallet : ethers . NonceManager , provider : ethers . JsonRpcProvider ) => {
153
168
// NOTE: It is OK if all of these roles are done via the funder wallet.
154
- const deployerAndManager = funderWallet ,
155
- randomPerson = funderWallet ;
169
+ const deployerAndManager = funderWallet ;
170
+ const deployerAndManagerAddress = await deployerAndManager . getAddress ( ) ;
171
+
172
+ const randomPerson = ethers . Wallet . createRandom ( ) . connect ( deployerAndManager . provider ) ;
173
+ const fundRandomPersonTx = await deployerAndManager . sendTransaction ( {
174
+ to : await randomPerson . getAddress ( ) ,
175
+ value : ethers . parseEther ( '1' ) ,
176
+ } ) ;
177
+ await fundRandomPersonTx . wait ( ) ;
156
178
157
179
// Deploy contracts
158
- const accessControlRegistryFactory = new AccessControlRegistryFactory ( deployerAndManager as Signer ) ;
180
+ const accessControlRegistryFactory = new AccessControlRegistryFactory ( deployerAndManager ) ;
159
181
const accessControlRegistry = await accessControlRegistryFactory . deploy ( ) ;
160
182
await accessControlRegistry . waitForDeployment ( ) ;
161
- const api3ServerV1Factory = new Api3ServerV1Factory ( deployerAndManager as Signer ) ;
183
+ const api3ServerV1Factory = new Api3ServerV1Factory ( deployerAndManager ) ;
162
184
const api3ServerV1AdminRoleDescription = 'Api3ServerV1 admin' ;
163
185
const api3ServerV1 = await api3ServerV1Factory . deploy (
164
- accessControlRegistry . getAddress ( ) ,
186
+ await accessControlRegistry . getAddress ( ) ,
165
187
api3ServerV1AdminRoleDescription ,
166
- deployerAndManager . address
188
+ deployerAndManagerAddress
167
189
) ;
168
190
await api3ServerV1 . waitForDeployment ( ) ;
169
- const airseekerRegistryFactory = new AirseekerRegistryFactory ( deployerAndManager as Signer ) ;
191
+ const airseekerRegistryFactory = new AirseekerRegistryFactory ( deployerAndManager ) ;
170
192
const airseekerRegistry = await airseekerRegistryFactory . deploy (
171
- await ( deployerAndManager as Signer ) . getAddress ( ) ,
172
- api3ServerV1 . getAddress ( )
193
+ deployerAndManagerAddress ,
194
+ await api3ServerV1 . getAddress ( )
173
195
) ;
174
196
await airseekerRegistry . waitForDeployment ( ) ;
175
197
@@ -179,25 +201,24 @@ export const deploy = async (funderWallet: ethers.HDNodeWallet, provider: ethers
179
201
const airnodeFeed1Wallet = ethers . Wallet . fromPhrase ( airnodeFeed1 . nodeSettings . airnodeWalletMnemonic ) . connect (
180
202
provider
181
203
) ;
182
- const airnodeFeed2Wallet = ethers . Wallet . fromPhrase ( airnodeFeed2 . nodeSettings . airnodeWalletMnemonic ) . connect (
183
- provider
184
- ) ;
204
+ const airnodeFeed1WalletAddress = await airnodeFeed1Wallet . getAddress ( ) ;
205
+ const airnodeFeed2Wallet = ethers . Wallet . fromPhrase ( airnodeFeed2 . nodeSettings . airnodeWalletMnemonic , provider ) ;
206
+ const airnodeFeed2WalletAddress = await airnodeFeed2Wallet . getAddress ( ) ;
185
207
const airnodeFeed1Beacons = Object . values ( airnodeFeed1 . templates ) . map ( ( template : any ) => {
186
- return deriveBeaconData ( { ...template , airnodeAddress : airnodeFeed1Wallet . address } ) ;
208
+ return deriveBeaconData ( { ...template , airnodeAddress : airnodeFeed1WalletAddress } ) ;
187
209
} ) ;
188
210
const airnodeFeed2Beacons = Object . values ( airnodeFeed2 . templates ) . map ( ( template : any ) => {
189
- return deriveBeaconData ( { ...template , airnodeAddress : airnodeFeed2Wallet . address } ) ;
211
+ return deriveBeaconData ( { ...template , airnodeAddress : airnodeFeed2WalletAddress } ) ;
190
212
} ) ;
191
213
192
214
// Set active dAPIs
193
215
const apiTreeValues = [
194
- [ airnodeFeed1Wallet . address , joinUrl ( airnodeFeed1 . signedApis [ 0 ] . url , 'default' ) ] , // NOTE: Airnode feed pushes to the "/" of the signed API, but we need to query it additional path.
195
- [ airnodeFeed2Wallet . address , joinUrl ( airnodeFeed2 . signedApis [ 0 ] . url , 'default' ) ] , // NOTE: Airnode feed pushes to the "/" of the signed API, but we need to query it additional path.
216
+ [ airnodeFeed1WalletAddress , joinUrl ( airnodeFeed1 . signedApis [ 0 ] . url , 'default' ) ] , // NOTE: Airnode feed pushes to the "/" of the signed API, but we need to query it additional path.
217
+ [ airnodeFeed2WalletAddress , joinUrl ( airnodeFeed2 . signedApis [ 0 ] . url , 'default' ) ] , // NOTE: Airnode feed pushes to the "/" of the signed API, but we need to query it additional path.
196
218
] as const ;
197
- let tx : ContractTransactionResponse ;
198
219
for ( const [ airnode , url ] of apiTreeValues ) {
199
- tx = await airseekerRegistry . connect ( deployerAndManager ) . setSignedApiUrl ( airnode , url ) ;
200
- await tx . wait ( ) ;
220
+ const setSignedApiUrlTx = await airseekerRegistry . connect ( deployerAndManager ) . setSignedApiUrl ( airnode , url ) ;
221
+ await setSignedApiUrlTx . wait ( ) ;
201
222
}
202
223
const dapiInfos = zip ( airnodeFeed1Beacons , airnodeFeed2Beacons ) . map ( ( [ airnodeFeed1Beacon , airnodeFeed2Beacon ] ) => {
203
224
return {
@@ -219,17 +240,19 @@ export const deploy = async (funderWallet: ethers.HDNodeWallet, provider: ethers
219
240
[ 'address[]' , 'bytes32[]' ] ,
220
241
[ airnodes , templateIds ]
221
242
) ;
222
- tx = await airseekerRegistry . connect ( randomPerson ) . registerDataFeed ( encodedBeaconSetData ) ;
223
- await tx . wait ( ) ;
243
+ const registerDataFeedTx = await airseekerRegistry . connect ( randomPerson ) . registerDataFeed ( encodedBeaconSetData ) ;
244
+ await registerDataFeedTx . wait ( ) ;
224
245
const HUNDRED_PERCENT = 1e8 ;
225
246
const deviationThresholdInPercentage = BigInt ( HUNDRED_PERCENT / 100 ) ; // 1%
226
247
const deviationReference = 0n ;
227
248
const heartbeatInterval = BigInt ( 86_400 ) ; // 24 hrs
228
- tx = await api3ServerV1 . connect ( deployerAndManager ) . setDapiName ( dapiName , beaconSetId ) ;
229
- await tx . wait ( ) ;
230
- tx = await airseekerRegistry . connect ( deployerAndManager ) . setDapiNameToBeActivated ( dapiName ) ;
231
- await tx . wait ( ) ;
232
- tx = await airseekerRegistry
249
+ const setDapiNameTx = await api3ServerV1 . connect ( deployerAndManager ) . setDapiName ( dapiName , beaconSetId ) ;
250
+ await setDapiNameTx . wait ( ) ;
251
+ const setDapiNameToBeActivatedTx = await airseekerRegistry
252
+ . connect ( deployerAndManager )
253
+ . setDapiNameToBeActivated ( dapiName ) ;
254
+ await setDapiNameToBeActivatedTx . wait ( ) ;
255
+ const setDapiNameUpdateParametersTx = await airseekerRegistry
233
256
. connect ( deployerAndManager )
234
257
. setDapiNameUpdateParameters (
235
258
dapiName ,
@@ -238,7 +261,7 @@ export const deploy = async (funderWallet: ethers.HDNodeWallet, provider: ethers
238
261
[ deviationThresholdInPercentage , deviationReference , heartbeatInterval ]
239
262
)
240
263
) ;
241
- await tx . wait ( ) ;
264
+ await setDapiNameUpdateParametersTx . wait ( ) ;
242
265
}
243
266
244
267
return {
@@ -265,10 +288,10 @@ async function main() {
265
288
polling : true ,
266
289
pollingInterval : 100 ,
267
290
} ) ;
268
- const funderWallet = ethers . Wallet . fromPhrase ( process . env . FUNDER_MNEMONIC ) . connect ( provider ) ;
291
+ const funderWallet = new NonceManager ( ethers . Wallet . fromPhrase ( process . env . FUNDER_MNEMONIC , provider ) ) ;
269
292
270
293
await refundFunder ( funderWallet ) ;
271
- const balance = await provider . getBalance ( funderWallet . address ) ;
294
+ const balance = await provider . getBalance ( await funderWallet . getAddress ( ) ) ;
272
295
console . info ( 'Funder balance:' , ethers . formatEther ( balance . toString ( ) ) ) ;
273
296
console . info ( ) ;
274
297
0 commit comments