Skip to content

Commit

Permalink
hardware wallets can now create safe multisig wallet tx proposals (#2038
Browse files Browse the repository at this point in the history
)

provide the ability to create safe multisig wallet transaction proposals via an sdk
  • Loading branch information
alysiahuggins authored Sep 20, 2024
1 parent 52ad3fa commit a078d8a
Show file tree
Hide file tree
Showing 6 changed files with 1,245 additions and 793 deletions.
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

0 comments on commit a078d8a

Please sign in to comment.