diff --git a/Makefile b/Makefile index bfd70521e..22937f60b 100644 --- a/Makefile +++ b/Makefile @@ -221,8 +221,6 @@ test-integration-soak-ci: test-integration-contracts: build-ts env-devnet-hardhat echo "Tests currently broken because of starknet-hardhat-plugin" exit 1 - cd packages-ts/integration-multisig/ && \ - yarn test cd packages-ts/starknet/ && \ yarn test diff --git a/contracts/src/tests/test_multisig.cairo b/contracts/src/tests/test_multisig.cairo index 8976eafa5..40c87a8e1 100644 --- a/contracts/src/tests/test_multisig.cairo +++ b/contracts/src/tests/test_multisig.cairo @@ -1,3 +1,5 @@ +use chainlink::multisig::IMultisigDispatcherTrait; +use core::traits::Into; use starknet::class_hash_const; use starknet::contract_address_const; use starknet::syscalls::deploy_syscall; @@ -13,6 +15,7 @@ use traits::TryInto; use chainlink::multisig::assert_unique_values; use chainlink::multisig::Multisig; use chainlink::multisig::Multisig::{MultisigImpl, UpgradeableImpl}; +use chainlink::multisig::{IMultisigDispatcher}; #[starknet::contract] mod MultisigTest { @@ -27,9 +30,6 @@ mod MultisigTest { } } -// TODO: test set_threshold with recursive call -// TODO: test set_signers with recursive call -// TODO: test set_signers_and_thershold with recursive call fn STATE() -> Multisig::ContractState { Multisig::contract_state_for_testing() @@ -434,3 +434,293 @@ fn test_execute_after_set_threshold() { set_caller_address(signer1); MultisigImpl::execute_transaction(ref state, nonce: 0); } + +// test set_threshold (non-recursive) +#[test] +fn test_set_threshold() { + let s1 = contract_address_const::<1>(); + let s2 = contract_address_const::<2>(); + let s3 = contract_address_const::<3>(); + let signers = array![s1, s2, s3]; + let init_threshold: usize = 3; + let new_threshold: usize = 2; + + let mut deploy_calldata = ArrayTrait::new(); + Serde::serialize(@signers, ref deploy_calldata); + Serde::serialize(@init_threshold, ref deploy_calldata); + let (multisig_address, _) = deploy_syscall( + Multisig::TEST_CLASS_HASH.try_into().unwrap(), 0, deploy_calldata.span(), false + ) + .unwrap(); + + let multisig = IMultisigDispatcher { contract_address: multisig_address }; + assert(multisig.get_threshold() == init_threshold, 'invalid init threshold'); + set_caller_address(multisig_address); + set_contract_address(multisig_address); + multisig.set_threshold(new_threshold); + assert(multisig.get_threshold() == new_threshold, 'threshold was not updated'); +} + +// test set_threshold with recursive call +#[test] +fn test_recursive_set_threshold() { + // Defines helper variables + let s1 = contract_address_const::<1>(); + let s2 = contract_address_const::<2>(); + let signers = array![s1, s2]; + let init_threshold: usize = 2; + let new_threshold: usize = 1; + + // Deploys the contract + let mut deploy_calldata = ArrayTrait::new(); + Serde::serialize(@signers, ref deploy_calldata); + Serde::serialize(@init_threshold, ref deploy_calldata); + let (multisig_address, _) = deploy_syscall( + Multisig::TEST_CLASS_HASH.try_into().unwrap(), 0, deploy_calldata.span(), false + ) + .unwrap(); + + // Gets a dispatcher (so we can call methods on the deployed contract) + let multisig = IMultisigDispatcher { contract_address: multisig_address }; + + // Checks that the threshold was correctly initialized on deployment + assert(multisig.get_threshold() == init_threshold, 'invalid init threshold'); + + // Recursive call occurs here - this code proposes a transaction to the + // multisig contract that calls the set_threshold function on the multisig + // contract. + let mut set_threshold_calldata = ArrayTrait::new(); + Serde::serialize(@new_threshold, ref set_threshold_calldata); + set_caller_address(multisig_address); + set_contract_address(multisig_address); + multisig + .submit_transaction(multisig_address, selector!("set_threshold"), set_threshold_calldata); + + // Signer 1 confirms the transaction + set_caller_address(s1); + set_contract_address(s1); + multisig.confirm_transaction(0); + + // Signer 2 confirms the transaction + set_caller_address(s2); + set_contract_address(s2); + multisig.confirm_transaction(0); + + // Once we have enough confirmations, we execute the transaction + set_caller_address(multisig_address); + set_contract_address(multisig_address); + multisig.execute_transaction(0); + + // Now we check that the threshold was actually updated + assert(multisig.get_threshold() == new_threshold, 'threshold was not updated'); +} + +// test set_signers (non-recursive) +#[test] +fn test_set_signers() { + let s1 = contract_address_const::<1>(); + let s2 = contract_address_const::<2>(); + let init_signers = array![s1, s2]; + let new_signers = array![s1]; + let threshold: usize = 2; + + let mut deploy_calldata = ArrayTrait::new(); + Serde::serialize(@init_signers, ref deploy_calldata); + Serde::serialize(@threshold, ref deploy_calldata); + let (multisig_address, _) = deploy_syscall( + Multisig::TEST_CLASS_HASH.try_into().unwrap(), 0, deploy_calldata.span(), false + ) + .unwrap(); + + let multisig = IMultisigDispatcher { contract_address: multisig_address }; + + let returned_signers = multisig.get_signers(); + assert(returned_signers.len() == 2, 'should match signers length'); + assert(*returned_signers.at(0) == s1, 'should match signer 1'); + assert(*returned_signers.at(1) == s2, 'should match signer 2'); + assert(multisig.get_threshold() == 2, 'wrong init threshold'); + + set_caller_address(multisig_address); + set_contract_address(multisig_address); + multisig.set_signers(new_signers); + + let updated_signers = multisig.get_signers(); + assert(updated_signers.len() == 1, 'should match signers length'); + assert(*updated_signers.at(0) == s1, 'should match signer 1'); + assert(multisig.get_threshold() == 1, 'threshold not updated'); +} + +// test set_signers with recursive call +#[test] +fn test_recursive_set_signers() { + // Defines helper variables + let s1 = contract_address_const::<1>(); + let s2 = contract_address_const::<2>(); + let init_signers = array![s1, s2]; + let new_signers = array![s1]; + let init_threshold: usize = 2; + + // Deploys the contract + let mut deploy_calldata = ArrayTrait::new(); + Serde::serialize(@init_signers, ref deploy_calldata); + Serde::serialize(@init_threshold, ref deploy_calldata); + let (multisig_address, _) = deploy_syscall( + Multisig::TEST_CLASS_HASH.try_into().unwrap(), 0, deploy_calldata.span(), false + ) + .unwrap(); + + // Gets a dispatcher (so we can call methods on the deployed contract) + let multisig = IMultisigDispatcher { contract_address: multisig_address }; + + // Checks that the signers were correctly initialized on deployment + let returned_signers = multisig.get_signers(); + assert(returned_signers.len() == 2, 'should match signers length'); + assert(*returned_signers.at(0) == s1, 'should match signer 1'); + assert(*returned_signers.at(1) == s2, 'should match signer 2'); + assert(multisig.get_threshold() == 2, 'wrong init threshold'); + + // Recursive call occurs here - this code proposes a transaction to the + // multisig contract that calls the set_signers function on the multisig + // contract. + let mut set_signers_calldata = ArrayTrait::new(); + Serde::serialize(@new_signers, ref set_signers_calldata); + set_caller_address(multisig_address); + set_contract_address(multisig_address); + multisig.submit_transaction(multisig_address, selector!("set_signers"), set_signers_calldata); + + // Signer 1 confirms the transaction + set_caller_address(s1); + set_contract_address(s1); + multisig.confirm_transaction(0); + + // Signer 2 confirms the transaction + set_caller_address(s2); + set_contract_address(s2); + multisig.confirm_transaction(0); + + // Once we have enough confirmations, we execute the transaction + set_caller_address(multisig_address); + set_contract_address(multisig_address); + multisig.execute_transaction(0); + + // Now we check that the signers were actually updated + let updated_signers = multisig.get_signers(); + assert(updated_signers.len() == 1, 'should match signers length'); + assert(*updated_signers.at(0) == s1, 'should match signer 1'); + assert(multisig.get_threshold() == 1, 'wrong threshold'); +} + +// test set_signers_and_threshold (non-recursive) +#[test] +fn test_set_signers_and_threshold() { + let s1 = contract_address_const::<1>(); + let s2 = contract_address_const::<2>(); + let s3 = contract_address_const::<3>(); + let init_signers = array![s1, s2, s3]; + let new_signers = array![s1, s2]; + let init_threshold: usize = 3; + let new_threshold: usize = 1; + + let mut deploy_calldata = ArrayTrait::new(); + Serde::serialize(@init_signers, ref deploy_calldata); + Serde::serialize(@init_threshold, ref deploy_calldata); + let (multisig_address, _) = deploy_syscall( + Multisig::TEST_CLASS_HASH.try_into().unwrap(), 0, deploy_calldata.span(), false + ) + .unwrap(); + + let multisig = IMultisigDispatcher { contract_address: multisig_address }; + + let returned_signers = multisig.get_signers(); + assert(returned_signers.len() == 3, 'should match signers length'); + assert(*returned_signers.at(0) == s1, 'should match signer 1'); + assert(*returned_signers.at(1) == s2, 'should match signer 2'); + assert(*returned_signers.at(2) == s3, 'should match signer 3'); + assert(multisig.get_threshold() == init_threshold, 'wrong init threshold'); + + set_caller_address(multisig_address); + set_contract_address(multisig_address); + multisig.set_signers_and_threshold(new_signers, new_threshold); + + let updated_signers = multisig.get_signers(); + assert(updated_signers.len() == 2, 'should match signers length'); + assert(*updated_signers.at(0) == s1, 'should match signer 1'); + assert(*updated_signers.at(1) == s2, 'should match signer 2'); + assert(multisig.get_threshold() == new_threshold, 'threshold not updated'); +} + +// test set_signers_and_threshold with recursive call +#[test] +fn test_recursive_set_signers_and_threshold() { + // Defines helper variables + let s1 = contract_address_const::<1>(); + let s2 = contract_address_const::<2>(); + let s3 = contract_address_const::<3>(); + let init_signers = array![s1, s2, s3]; + let new_signers = array![s1, s2]; + let init_threshold: usize = 3; + let new_threshold: usize = 1; + + // Deploys the contract + let mut deploy_calldata = ArrayTrait::new(); + Serde::serialize(@init_signers, ref deploy_calldata); + Serde::serialize(@init_threshold, ref deploy_calldata); + let (multisig_address, _) = deploy_syscall( + Multisig::TEST_CLASS_HASH.try_into().unwrap(), 0, deploy_calldata.span(), false + ) + .unwrap(); + + // Gets a dispatcher (so we can call methods on the deployed contract) + let multisig = IMultisigDispatcher { contract_address: multisig_address }; + + // Checks that the initial state is correct + let returned_signers = multisig.get_signers(); + assert(returned_signers.len() == 3, 'should match signers length'); + assert(*returned_signers.at(0) == s1, 'should match signer 1'); + assert(*returned_signers.at(1) == s2, 'should match signer 2'); + assert(*returned_signers.at(2) == s3, 'should match signer 3'); + assert(multisig.get_threshold() == 3, 'wrong init threshold'); + + // Recursive call occurs here - this code proposes a transaction to the + // multisig contract that calls the set_signers_and_threshold function + // on the multisig contract. + let mut set_signers_and_threshold_calldata = ArrayTrait::new(); + Serde::serialize(@new_signers, ref set_signers_and_threshold_calldata); + Serde::serialize(@new_threshold, ref set_signers_and_threshold_calldata); + set_caller_address(multisig_address); + set_contract_address(multisig_address); + multisig + .submit_transaction( + multisig_address, + selector!("set_signers_and_threshold"), + set_signers_and_threshold_calldata + ); + + // Signer 1 confirms the transaction + set_caller_address(s1); + set_contract_address(s1); + multisig.confirm_transaction(0); + + // Signer 2 confirms the transaction + set_caller_address(s2); + set_contract_address(s2); + multisig.confirm_transaction(0); + + // Signer 3 confirms the transaction + set_caller_address(s3); + set_contract_address(s3); + multisig.confirm_transaction(0); + + // Once we have enough confirmations, we execute the transaction + set_caller_address(multisig_address); + set_contract_address(multisig_address); + multisig.execute_transaction(0); + + // Now we check that the signers were actually updated + let updated_signers = multisig.get_signers(); + assert(updated_signers.len() == 2, 'should match signers length'); + assert(*updated_signers.at(0) == s1, 'should match signer 1'); + assert(*updated_signers.at(1) == s2, 'should match signer 2'); + assert(multisig.get_threshold() == 1, 'wrong threshold'); +} + diff --git a/packages-ts/integration-multisig/hardhat.config.ts b/packages-ts/integration-multisig/hardhat.config.ts deleted file mode 100644 index 88fe0f990..000000000 --- a/packages-ts/integration-multisig/hardhat.config.ts +++ /dev/null @@ -1,41 +0,0 @@ -import path from 'path' -import { HardhatUserConfig } from 'hardhat/types' -import '@shardlabs/starknet-hardhat-plugin' -import { prepareHardhatArtifacts } from '../../contracts/test/setup' - -/** - * @type import('hardhat/config').HardhatUserConfig - */ -const config: HardhatUserConfig = { - starknet: { - venv: 'active', - network: 'devnet', - wallets: { - OpenZeppelin: { - accountName: 'OpenZeppelin', - modulePath: 'starkware.starknet.wallets.open_zeppelin.OpenZeppelinAccount', - accountPath: '~/.starknet_accounts', - }, - }, - }, - networks: { - devnet: { - url: 'http://127.0.0.1:5050/', - args: ['--cairo-compiler-manifest', '../../vendor/cairo/Cargo.toml'], - }, - }, - mocha: { - timeout: 10000000, - rootHooks: { - beforeAll: prepareHardhatArtifacts, - }, - }, - paths: { - sources: './solidity', - starknetSources: '../../contracts/src', - starknetArtifacts: '../../contracts/target/release', - cairoPaths: [], - }, -} - -export default config diff --git a/packages-ts/integration-multisig/package.json b/packages-ts/integration-multisig/package.json deleted file mode 100644 index bd2b8aeeb..000000000 --- a/packages-ts/integration-multisig/package.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "@chainlink/starknet-integration-tests-multisig", - "version": "1.0.0", - "description": "Multisig integration test", - "main": "index.js", - "directories": { - "test": "test" - }, - "scripts": { - "test": "npx hardhat --network localhost test" - }, - "devDependencies": { - "@changesets/cli": "^2.22.0", - "@nomiclabs/hardhat-ethers": "^2.0.5", - "@shardlabs/starknet-hardhat-plugin": "^0.8.0-alpha.2", - "@types/chai": "^4.3.3", - "@types/elliptic": "^6.4.14", - "@types/mocha": "^9.1.1", - "chai": "^4.3.6", - "ethers": "^5.6.8", - "hardhat": "^2.16.1", - "starsign-multisig": "^0.3.0" - } -} diff --git a/packages-ts/integration-multisig/test/Multisig.test.ts b/packages-ts/integration-multisig/test/Multisig.test.ts deleted file mode 100644 index 47bb825ab..000000000 --- a/packages-ts/integration-multisig/test/Multisig.test.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { expect } from 'chai' -import { starknet } from 'hardhat' -import { StarknetContract, Account } from 'hardhat/types/runtime' -import { num, hash } from 'starknet' -import { account, expectSuccessOrDeclared } from '@chainlink/starknet' - -describe('Multisig integration tests', function () { - this.timeout(300_000) - - const opts = account.makeFunderOptsFromEnv() - const funder = new account.Funder(opts) - - let account1: Account - let account2: Account - let account3: Account - - let multisig: StarknetContract - - before(async function () { - account1 = await starknet.OpenZeppelinAccount.createAccount() - account2 = await starknet.OpenZeppelinAccount.createAccount() - account3 = await starknet.OpenZeppelinAccount.createAccount() - - await funder.fund([ - { account: account1.address, amount: 1e21 }, - { account: account2.address, amount: 1e21 }, - { account: account3.address, amount: 1e21 }, - ]) - await account1.deployAccount() - await account2.deployAccount() - await account3.deployAccount() - }) - - it('Deploy contract', async () => { - let multisigFactory = await starknet.getContractFactory('multisig') - await expectSuccessOrDeclared(account1.declare(multisigFactory, { maxFee: 1e20 })) - - multisig = await account1.deploy(multisigFactory, { - signers: [ - num.toBigInt(account1.starknetContract.address), - num.toBigInt(account2.starknetContract.address), - num.toBigInt(account3.starknetContract.address), - ], - threshold: 2, - }) - - expect(multisig).to.be.ok - }) - - it('should submit & confirm transaction', async () => { - const nonce = 0 - const newThreshold = 1n - const selector = hash.getSelectorFromName('set_threshold') - - const payload = { - to: multisig.address, - function_selector: selector, - calldata: [newThreshold], - } - - { - const res = await account1.invoke(multisig, 'submit_transaction', payload) - const txReciept = await starknet.getTransactionReceipt(res) - - expect(txReciept.events.length).to.equal(2) - expect(txReciept.events[0].data.length).to.equal(3) - expect(txReciept.events[0].data[1]).to.equal(num.toHex(num.toBigInt(nonce, 'hex'))) - } - - await account1.invoke(multisig, 'confirm_transaction', { - nonce, - }) - - await account2.invoke(multisig, 'confirm_transaction', { - nonce, - }) - - await account3.invoke(multisig, 'execute_transaction', { - nonce, - }) - - { - const res = await multisig.call('get_threshold') - expect(res.response).to.equal(newThreshold) - } - }) -}) diff --git a/packages-ts/integration-multisig/tsconfig.json b/packages-ts/integration-multisig/tsconfig.json deleted file mode 100644 index 8ac80569b..000000000 --- a/packages-ts/integration-multisig/tsconfig.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "include": ["./test"], - "files": ["./hardhat.config.ts"] - } - \ No newline at end of file