1- import { ethers } from "hardhat" ;
1+ import { ethers , upgrades } from "hardhat" ;
22import * as helpers from "@nomicfoundation/hardhat-toolbox/network-helpers" ;
33import { changeMultisigOwner } from "../scripts/deployment-utils/change-multisig-owner" ;
44import { expect } from "chai" ;
55import { DeploymentConfig , read } from "../scripts/deployment-utils/deploy" ;
66import multsigInfoJson from "../multisig-owners.json" ;
7+ import {
8+ GnosisSafe ,
9+ LiquidityBridgeContract ,
10+ LiquidityBridgeContractAdmin ,
11+ LiquidityBridgeContractV2 ,
12+ } from "../typechain-types" ;
13+ import { deployUpgradeLibraries } from "../scripts/deployment-utils/upgrade-proxy" ;
714
815type MultisigInfo = Record <
916 string ,
@@ -14,33 +21,29 @@ type MultisigInfo = Record<
1421> ;
1522
1623const { FORK_NETWORK_NAME } = process . env ;
17-
1824const multsigInfo : MultisigInfo = multsigInfoJson ;
1925
20- describe ( "Should change LBC owner to the multisig" , function ( ) {
26+ describe ( "Should change LBC owner to the multisig.ts " , function ( ) {
2127 it ( "Should change the owner" , async ( ) => {
2228 await checkForkedNetwork ( ) ;
23-
2429 const networkName = FORK_NETWORK_NAME ?? "rskTestnet" ;
2530
31+ const lbcName = "LiquidityBridgeContract" ;
2632 const addresses : Partial < DeploymentConfig > = read ( ) ;
2733 const networkDeployments : Partial < DeploymentConfig [ string ] > | undefined =
2834 addresses [ networkName ] ;
29-
3035 const lbcAddress = networkDeployments ?. LiquidityBridgeContract ?. address ;
36+ const safeAddress = multsigInfo [ networkName ] . address ;
3137
3238 if ( ! lbcAddress ) {
3339 throw new Error (
3440 "LiquidityBridgeContract proxy deployment info not found"
3541 ) ;
3642 }
43+ console . info ( `LBC address: ${ lbcAddress } ` ) ;
44+ console . info ( `Safe address: ${ safeAddress } ` ) ;
3745
38- const lbc = await ethers . getContractAt (
39- "LiquidityBridgeContractV2" ,
40- lbcAddress
41- ) ;
42-
43- const safeAddress = multsigInfo [ networkName ] . address ;
46+ const lbc = await ethers . getContractAt ( lbcName , lbcAddress ) ;
4447
4548 const lbcOwner = await lbc . owner ( ) ;
4649 console . info ( "LBC owner:" , lbcOwner ) ;
@@ -52,6 +55,57 @@ describe("Should change LBC owner to the multisig", function () {
5255 ) . to . not . be . reverted ;
5356 const newLbcOwner = await lbc . owner ( ) ;
5457 console . info ( "New LBC owner:" , newLbcOwner ) ;
58+
59+ await expect (
60+ lbc . connect ( impersonatedSigner ) . setProviderStatus ( 1 , false )
61+ ) . to . be . revertedWith ( "LBC005" ) ;
62+
63+ const safeContract = await ethers . getContractAt ( "GnosisSafe" , safeAddress ) ;
64+ const opts = { verbose : true } ;
65+ const libs = await deployUpgradeLibraries ( networkName , opts ) ;
66+
67+ const NewLbcFactory = await ethers . getContractFactory (
68+ "LiquidityBridgeContractV2" ,
69+ {
70+ libraries : {
71+ QuotesV2 : libs . quotesV2 ,
72+ BtcUtils : libs . btcUtils ,
73+ SignatureValidator : libs . signatureValidator ,
74+ } ,
75+ }
76+ ) ;
77+
78+ const newLbcDeployed = await NewLbcFactory . deploy ( ) ;
79+ const newLbcAddress = await newLbcDeployed . getAddress ( ) ;
80+
81+ await expect (
82+ multisigExecProviderStatusChangeTransaction ( safeContract , lbc )
83+ ) . to . eventually . be . equal ( true ) ;
84+
85+ const adminAddress = await upgrades . erc1967 . getAdminAddress ( lbcAddress ) ;
86+ const adminContract = await ethers . getContractAt (
87+ "LiquidityBridgeContractAdmin" ,
88+ adminAddress
89+ ) ;
90+
91+ await expect (
92+ adminContract . upgrade ( lbcAddress , newLbcAddress )
93+ ) . to . revertedWith ( "Ownable: caller is not the owner" ) ;
94+
95+ await expect (
96+ multisigExecUpgradeTransaction (
97+ safeContract ,
98+ lbc ,
99+ newLbcDeployed ,
100+ adminContract
101+ )
102+ ) . to . eventually . be . equal ( true ) ;
103+
104+ const newLbc = await ethers . getContractAt (
105+ "LiquidityBridgeContractV2" ,
106+ lbcAddress
107+ ) ;
108+ await expect ( newLbc . version ( ) ) . to . eventually . be . equal ( "1.3.0" ) ;
55109 } ) ;
56110} ) ;
57111
@@ -62,3 +116,212 @@ async function checkForkedNetwork() {
62116 console . error ( "Not a forked network:" , error ) ;
63117 }
64118}
119+
120+ function generateConcatenatedSignatures ( owners : string [ ] ) {
121+ const concatenatedSignatures =
122+ "0x" +
123+ owners
124+ . sort ( ( a , b ) => a . toLowerCase ( ) . localeCompare ( b . toLowerCase ( ) ) ) // SORT owners in ascending order
125+ . map ( ( owner ) => {
126+ return "0" . repeat ( 24 ) + owner . slice ( 2 ) + "0" . repeat ( 64 ) + "01" ;
127+ } )
128+ . join ( "" ) ;
129+
130+ return concatenatedSignatures ;
131+ }
132+
133+ export async function multisigExecProviderStatusChangeTransaction (
134+ safeContract : GnosisSafe ,
135+ lbc : LiquidityBridgeContract
136+ ) : Promise < boolean > {
137+ const callData = lbc . interface . encodeFunctionData ( "setProviderStatus" , [
138+ 1 ,
139+ false ,
140+ ] ) ;
141+ console . info ( "Call data:" , callData ) ;
142+
143+ const nonce = await safeContract . nonce ( ) ;
144+ console . info ( "Nonce:" , nonce ) ;
145+
146+ const txData = {
147+ to : await lbc . getAddress ( ) ,
148+ value : 0 ,
149+ data : callData ,
150+ operation : 0 ,
151+ safeTxGas : 0 ,
152+ baseGas : 0 ,
153+ gasPrice : 0 ,
154+ gasToken : ethers . ZeroAddress ,
155+ refundReceiver : ethers . ZeroAddress ,
156+ nonce : nonce ,
157+ signatures : "0x" ,
158+ } ;
159+
160+ const owners = await safeContract . getOwners ( ) ;
161+
162+ const desiredBalance = ethers . toQuantity ( ethers . parseEther ( "100" ) ) ;
163+
164+ await helpers . impersonateAccount ( owners [ 0 ] ) ;
165+ const impersonateOwner1 = await ethers . getSigner ( owners [ 0 ] ) ;
166+ await ethers . provider . send ( "hardhat_setBalance" , [
167+ impersonateOwner1 . address ,
168+ desiredBalance ,
169+ ] ) ;
170+ await helpers . impersonateAccount ( owners [ 1 ] ) ;
171+ const impersonateOwner2 = await ethers . getSigner ( owners [ 1 ] ) ;
172+ await ethers . provider . send ( "hardhat_setBalance" , [
173+ impersonateOwner2 . address ,
174+ desiredBalance ,
175+ ] ) ;
176+ await helpers . impersonateAccount ( owners [ 2 ] ) ;
177+ const impersonateOwner3 = await ethers . getSigner ( owners [ 2 ] ) ;
178+ await ethers . provider . send ( "hardhat_setBalance" , [
179+ impersonateOwner3 . address ,
180+ desiredBalance ,
181+ ] ) ;
182+
183+ const transactionHash = await safeContract
184+ . connect ( impersonateOwner1 )
185+ . getTransactionHash (
186+ txData . to ,
187+ txData . value ,
188+ txData . data ,
189+ txData . operation ,
190+ txData . safeTxGas ,
191+ txData . baseGas ,
192+ txData . gasPrice ,
193+ txData . gasToken ,
194+ txData . refundReceiver ,
195+ txData . nonce
196+ ) ;
197+ console . info ( "Transaction hash:" , transactionHash ) ;
198+
199+ await safeContract . connect ( impersonateOwner1 ) . approveHash ( transactionHash ) ;
200+ await safeContract . connect ( impersonateOwner2 ) . approveHash ( transactionHash ) ;
201+ await safeContract . connect ( impersonateOwner3 ) . approveHash ( transactionHash ) ;
202+ const signature = generateConcatenatedSignatures ( [
203+ impersonateOwner1 . address ,
204+ impersonateOwner2 . address ,
205+ impersonateOwner3 . address ,
206+ ] ) ;
207+ console . info ( "Signature:" , signature ) ;
208+
209+ txData . signatures = signature ;
210+
211+ const result = await safeContract . execTransaction (
212+ txData . to ,
213+ txData . value ,
214+ txData . data ,
215+ txData . operation ,
216+ txData . safeTxGas ,
217+ txData . baseGas ,
218+ txData . gasPrice ,
219+ txData . gasToken ,
220+ txData . refundReceiver ,
221+ txData . signatures
222+ ) ;
223+
224+ return Boolean ( result ) ;
225+ }
226+
227+ export async function multisigExecUpgradeTransaction (
228+ safeContract : GnosisSafe ,
229+ lbc : LiquidityBridgeContract ,
230+ lbcV2 : LiquidityBridgeContractV2 ,
231+ adminContract : LiquidityBridgeContractAdmin
232+ ) : Promise < boolean > {
233+ const proxyAddress = await lbc . getAddress ( ) ;
234+
235+ let result = false ;
236+
237+ const callData = adminContract . interface . encodeFunctionData ( "upgrade" , [
238+ proxyAddress ,
239+ await lbcV2 . getAddress ( ) ,
240+ ] ) ;
241+ console . info ( "Call data:" , callData ) ;
242+
243+ const nonce = await safeContract . nonce ( ) ;
244+ console . info ( "Nonce:" , nonce ) ;
245+
246+ const txData = {
247+ to : await adminContract . getAddress ( ) ,
248+ value : 0 ,
249+ data : callData ,
250+ operation : 0 ,
251+ safeTxGas : 0 ,
252+ baseGas : 0 ,
253+ gasPrice : 0 ,
254+ gasToken : ethers . ZeroAddress ,
255+ refundReceiver : ethers . ZeroAddress ,
256+ nonce : nonce ,
257+ signatures : "0x" ,
258+ } ;
259+
260+ const owners = await safeContract . getOwners ( ) ;
261+
262+ const desiredBalance = ethers . toQuantity ( ethers . parseEther ( "100" ) ) ;
263+
264+ await helpers . impersonateAccount ( owners [ 0 ] ) ;
265+ const impersonateOwner1 = await ethers . getSigner ( owners [ 0 ] ) ;
266+ await ethers . provider . send ( "hardhat_setBalance" , [
267+ impersonateOwner1 . address ,
268+ desiredBalance ,
269+ ] ) ;
270+ await helpers . impersonateAccount ( owners [ 1 ] ) ;
271+ const impersonateOwner2 = await ethers . getSigner ( owners [ 1 ] ) ;
272+ await ethers . provider . send ( "hardhat_setBalance" , [
273+ impersonateOwner2 . address ,
274+ desiredBalance ,
275+ ] ) ;
276+ await helpers . impersonateAccount ( owners [ 2 ] ) ;
277+ const impersonateOwner3 = await ethers . getSigner ( owners [ 2 ] ) ;
278+ await ethers . provider . send ( "hardhat_setBalance" , [
279+ impersonateOwner3 . address ,
280+ desiredBalance ,
281+ ] ) ;
282+
283+ const transactionHash = await safeContract
284+ . connect ( impersonateOwner1 )
285+ . getTransactionHash (
286+ txData . to ,
287+ txData . value ,
288+ txData . data ,
289+ txData . operation ,
290+ txData . safeTxGas ,
291+ txData . baseGas ,
292+ txData . gasPrice ,
293+ txData . gasToken ,
294+ txData . refundReceiver ,
295+ txData . nonce
296+ ) ;
297+ console . info ( "Transaction hash:" , transactionHash ) ;
298+
299+ await safeContract . connect ( impersonateOwner1 ) . approveHash ( transactionHash ) ;
300+ await safeContract . connect ( impersonateOwner2 ) . approveHash ( transactionHash ) ;
301+ await safeContract . connect ( impersonateOwner3 ) . approveHash ( transactionHash ) ;
302+ const signature = generateConcatenatedSignatures ( [
303+ impersonateOwner1 . address ,
304+ impersonateOwner2 . address ,
305+ impersonateOwner3 . address ,
306+ ] ) ;
307+ console . info ( "Signature:" , signature ) ;
308+
309+ txData . signatures = signature ;
310+
311+ result = Boolean (
312+ await safeContract . execTransaction (
313+ txData . to ,
314+ txData . value ,
315+ txData . data ,
316+ txData . operation ,
317+ txData . safeTxGas ,
318+ txData . baseGas ,
319+ txData . gasPrice ,
320+ txData . gasToken ,
321+ txData . refundReceiver ,
322+ txData . signatures
323+ )
324+ ) ;
325+
326+ return Boolean ( result ) ;
327+ }
0 commit comments