Skip to content

Commit 495faee

Browse files
authored
Merge pull request #317 from rsksmart/feature/GBI-2478-addMultisigExecTransaction
feat: extended multisig change unit tests
2 parents e2ba3b7 + 43d9aad commit 495faee

File tree

2 files changed

+279
-16
lines changed

2 files changed

+279
-16
lines changed

e2e/multisig-migration.test.ts

Lines changed: 274 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
1-
import { ethers } from "hardhat";
1+
import { ethers, upgrades } from "hardhat";
22
import * as helpers from "@nomicfoundation/hardhat-toolbox/network-helpers";
33
import { changeMultisigOwner } from "../scripts/deployment-utils/change-multisig-owner";
44
import { expect } from "chai";
55
import { DeploymentConfig, read } from "../scripts/deployment-utils/deploy";
66
import 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

815
type MultisigInfo = Record<
916
string,
@@ -14,33 +21,29 @@ type MultisigInfo = Record<
1421
>;
1522

1623
const { FORK_NETWORK_NAME } = process.env;
17-
1824
const 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+
}

scripts/deployment-utils/change-multisig-owner.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,24 @@ import multisigOwners from "../../multisig-owners.json";
44
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
55

66
/**
7-
* Changes the multisig owner of the `LiquidityBridgeContract` deployed on the current network to the safe wallet
7+
* Changes the multisig.ts owner of the `LiquidityBridgeContract` deployed on the current network to the safe wallet
88
* provided.
99
*
1010
* This function validates the provided `newOwner` address, ensures ownership configuration matches
11-
* expectations, and performs the transfer of ownership to the new multisig address. Additionally,
11+
* expectations, and performs the transfer of ownership to the new multisig.ts address. Additionally,
1212
* it updates both the contract and proxy admin ownership.
1313
*
1414
* @async
15-
* @param {string} newOwner - The address of the new multisig owner (Safe contract).
15+
* @param {string} newOwner - The address of the new multisig.ts owner (Safe contract).
1616
* @param {string} network - The network where the script will run, by default will be the environment network.
1717
* @param {HardhatEthersSigner} signer - Optional signer for test.
1818
* @throws {Error} If the proxy contract is not deployed on the current network.
19-
* @throws {Error} If the provided `newOwner` address is not a valid multisig Safe contract.
19+
* @throws {Error} If the provided `newOwner` address is not a valid multisig.ts Safe contract.
2020
* @throws {Error} If the configuration of owners on the Safe does not match the expected configuration.
2121
* @returns {Promise<void>} Resolves when the ownership transfer process is complete.
2222
*
2323
* @example
24-
* // Change the multisig owner of the contract
24+
* // Change the multisig.ts owner of the contract
2525
* const newMultisigAddress = "0xNewSafeAddress";
2626
* await changeMultisigOwner(newMultisigAddress);
2727
*/

0 commit comments

Comments
 (0)