Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

hardware wallets can now create safe multisig wallet tx proposals #2038

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion contracts/script/multisigTransactionProposals/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,14 @@ To enable the permissioned prover on the light client contract, ensure that the
in the `.env.contracts` file:

- `RPC_URL`
- `SAFE_ORCHESTRATOR_PRIVATE_KEY`
- `SAFE_ORCHESTRATOR_PRIVATE_KEY` (if not using a hardware wallet)
- `SAFE_MULTISIG_ADDRESS`
- `APPROVED_PROVER_ADDRESS`
- `LIGHT_CLIENT_PROXY_CONTRACT_ADDRESS`
- `USE_HARDWARE_WALLET` (if yes, put "true", otherwise "false")

> **_NOTE:_** the signer for this transaction must be one of the signers in the **Safe Multisig Wallet**, whether the
> ledger hardware wallet or the address owned by `SAFE_ORCHESTRATOR_PRIVATE_KEY`

Assuming you're in the root folder, run the following command:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ethers } from "ethers";
import { EthersAdapter } from "@safe-global/protocol-kit";
import SafeApiKit from "@safe-global/api-kit";
import Safe from "@safe-global/protocol-kit";
import { getEnvVar, createSafeTransactionData, validateEthereumAddress } from "./utils";
import { getEnvVar, createSafeTransactionData, validateEthereumAddress, getSigner } from "./utils";
const SET_PROVER_CMD = "setProver" as const;
const DISABLE_PROVER_CMD = "disableProver" as const;

Expand All @@ -21,8 +21,9 @@ async function main() {
*/
// Initialize web3 provider using the RPC URL from environment variables
const web3Provider = new ethers.JsonRpcProvider(getEnvVar("RPC_URL"));
// Create a signer using the orchestrator's private key and the web3 provider
const orchestratorSigner = new ethers.Wallet(getEnvVar("SAFE_ORCHESTRATOR_PRIVATE_KEY"), web3Provider);

// Get the signer, this signer must be one of the signers on the Safe Multisig Wallet
const orchestratorSigner = getSigner(web3Provider);

// Set up Eth Adapter with ethers and the signer
const ethAdapter = new EthersAdapter({
Expand Down Expand Up @@ -200,9 +201,12 @@ async function createSafeTransaction(
// Prepare the safe transaction data with the contract address, data, and value
let safeTransactionData = createSafeTransactionData(contractAddress, data, value);

if (getEnvVar("USE_HARDWARE_WALLET")) {
console.log(`Please sign the message on your connected Ledger device`);
}

// Create the safe transaction using the Safe SDK
const safeTransaction = await safeSDK.createTransaction({ transactions: [safeTransactionData] });

return safeTransaction;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ethers } from "ethers";
import { EthersAdapter } from "@safe-global/protocol-kit";
import SafeApiKit from "@safe-global/api-kit";
import Safe from "@safe-global/protocol-kit";
import { getEnvVar, createSafeTransactionData, validateEthereumAddress } from "./utils";
import { getEnvVar, createSafeTransactionData, validateEthereumAddress, getSigner } from "./utils";
const UPGRADE_PROXY_CMD = "upgradeProxy" as const;

// declaring the type returned by the createTransaction method in the safe package locally (since the return type isn't exposed) so that if it's updated, it's reflected here too
Expand All @@ -23,8 +23,9 @@ async function main() {

// Initialize web3 provider using the RPC URL from environment variables
const web3Provider = new ethers.JsonRpcProvider(getEnvVar("RPC_URL"));
// Create a signer using the orchestrator's private key and the web3 provider
const orchestratorSigner = new ethers.Wallet(getEnvVar("SAFE_ORCHESTRATOR_PRIVATE_KEY"), web3Provider);

// Get the signer, this signer must be one of the signers on the Safe Multisig Wallet
const orchestratorSigner = getSigner(web3Provider);

// Set up Eth Adapter with ethers and the signer
const ethAdapter = new EthersAdapter({
Expand Down Expand Up @@ -75,7 +76,7 @@ function processCommandLineArguments(): UpgradeData {
}

/**
* Function to propose the transaction data for upgrading the new implemeantation
* Function to propose the transaction data for upgrading the new implementation
* @param {string} safeSDK - An instance of the Safe SDK
* @param {string} safeService - An instance of the Safe Service
* @param {string} signerAddress - The address of the address signing the transaction
Expand Down Expand Up @@ -145,6 +146,10 @@ async function createSafeTransaction(
// Prepare the safe transaction data with the contract address, data, and value
let safeTransactionData = createSafeTransactionData(contractAddress, data, value);

if (getEnvVar("USE_HARDWARE_WALLET")) {
console.log(`Please sign the message on your connected Ledger device`);
}

// Create the safe transaction using the Safe SDK
const safeTransaction = await safeSDK.createTransaction({ transactions: [safeTransactionData] });

Expand Down
21 changes: 21 additions & 0 deletions contracts/script/multisigTransactionProposals/safeSDK/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { ethers } from "ethers"; // Import ethers from the ethers library
import { LedgerSigner } from "@ethers-ext/signer-ledger";
import HIDTransport from "@ledgerhq/hw-transport-node-hid";

/**
* Function to check if a given string is a valid Ethereum address
Expand Down Expand Up @@ -50,3 +52,22 @@ export function createSafeTransactionData(to: string, data: string, value: strin
};
return safeTransactionData; // Return the safe transaction data object
}

/**
* Function to check if a given string is a valid Ethereum address
* @param {string} address - The Ethereum address to validate
* @throws {Error} - Throws an error if the address is invalid and doesn't follow Ethereum address standards
*/
export function getSigner(web3Provider: ethers.Provider): ethers.Signer {
let orchestratorSigner;
const use_hardware_wallet = getEnvVar("USE_HARDWARE_WALLET");
if (use_hardware_wallet == "true") {
// Create a signer using the ledger
orchestratorSigner = new LedgerSigner(HIDTransport, web3Provider);
} else {
// Create a signer using the orchestrator's private key and the web3 provider
orchestratorSigner = new ethers.Wallet(getEnvVar("SAFE_ORCHESTRATOR_PRIVATE_KEY"), web3Provider);
}

return orchestratorSigner;
}
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
"typescript": "^5.4.5"
},
"dependencies": {
"@ethers-ext/signer-ledger": "^6.0.0-beta.1",
"@ledgerhq/hw-transport-node-hid": "^6.29.4",
"@safe-global/api-kit": "^2.3.1",
"@safe-global/protocol-kit": "^3.1.0",
"dotenv": "^16.4.5",
Expand Down
Loading
Loading