-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #321 from tokamak-network/feat/set-safe-for-DAO
- Loading branch information
Showing
6 changed files
with
141 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
export enum Chains { | ||
Mainnet = 1, | ||
Sepolia = 11155111, | ||
LocalDevnet = 900, | ||
} | ||
|
||
/** | ||
* Get DAO members with chain id | ||
* | ||
* @param chainid - Chain ID | ||
* @returns [foundation, dao] Pre-defined owner addresses | ||
* @throws Throw an error if the chain is not supported. | ||
*/ | ||
export const getDAOMembers = (chainid: number): [string, string] => { | ||
if (chainid === Chains.Mainnet) { | ||
// Mainnet | ||
return [ | ||
'0x0Fd5632f3b52458C31A2C3eE1F4b447001872Be9', | ||
'0x61dc95E5f27266b94805ED23D95B4C9553A3D049', | ||
// '0xDD9f0cCc044B0781289Ee318e5971b0139602C26', // TokamakDAO | ||
] | ||
} else if (chainid === Chains.Sepolia) { | ||
// Sepolia | ||
return [ | ||
'0x0Fd5632f3b52458C31A2C3eE1F4b447001872Be9', | ||
'0x61dc95E5f27266b94805ED23D95B4C9553A3D049', | ||
// '0xA2101482b28E3D99ff6ced517bA41EFf4971a386', // TokamakDAO | ||
] | ||
} else if (chainid === Chains.LocalDevnet) { | ||
// LocalDevnet (for testing) | ||
return [ | ||
'0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65', // devAccount-4 | ||
'0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc', // devAccount-5 | ||
] | ||
} else { | ||
throw new Error('It is not a supported chain') | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import { ethers } from 'ethers' | ||
import { task, types } from 'hardhat/config' | ||
import { executeContractCallWithSigners } from '@tokamak-network/thanos-contracts/lib/safe-contracts/src/utils/execution' | ||
|
||
import { getDAOMembers } from '../src/utils/owners' | ||
|
||
/** | ||
* Adds the specified owner to the Gnosis Safe and verifies that the owner has been added. | ||
* | ||
* @param safeContract - The Gnosis Safe contract instance (with signer connected) | ||
* @param owner - The owner address to add | ||
* @param threshold - The threshold value to apply | ||
* @param signers - An array of signers (typically a single signer) | ||
*/ | ||
export const addOwnerAndVerify = async ( | ||
safeContract: ethers.Contract, | ||
owner: string, | ||
threshold: number, | ||
signers: ethers.Signer[] | ||
): Promise<void> => { | ||
// Execute addOwnerWithThreshold | ||
const tx = await executeContractCallWithSigners( | ||
safeContract, | ||
safeContract, | ||
'addOwnerWithThreshold', | ||
[owner, threshold], | ||
signers | ||
) | ||
console.log(`Tx Hash for adding owner ${owner}:`, tx.hash) | ||
await tx.wait() | ||
|
||
// Get Safe owner | ||
const safeOwners = await safeContract.getOwners() | ||
console.log(`Safe owners after adding owner ${owner}:`, safeOwners) | ||
if (safeOwners.includes(owner)) { | ||
console.log(`Successfully added owner: ${owner}`) | ||
} else { | ||
console.log(`Failed to add owner: ${owner}`) | ||
} | ||
} | ||
|
||
// Task | ||
task('set-safe-wallet', 'Set Safe Wallet for the TokamakDAO') | ||
.addParam('rpc', 'L1 RPC endpoint', '', types.string) | ||
.addParam('chainid', 'L1 chain id', '', types.int) | ||
.addParam('privatekey', 'Admin Private key', '', types.string) | ||
.addParam('address', 'Gnosis Safe contract address', '', types.string) | ||
.setAction(async (args) => { | ||
const l1Provider = new ethers.providers.StaticJsonRpcProvider(args.rpc) | ||
|
||
// Create the signer | ||
// TODO: Get the Admin's private key from the seed phrase (user's input) | ||
const ownerAPrivateKey = args.privatekey | ||
const signer = new ethers.Wallet(ownerAPrivateKey, l1Provider) | ||
|
||
// Get predefined owners | ||
const owners = getDAOMembers(args.chainid) | ||
|
||
// ABIs of Gnosis Safe | ||
const gnosisSafeAbi = [ | ||
'function getThreshold() view returns (uint256)', | ||
'function addOwnerWithThreshold(address owner, uint256 _threshold) external', | ||
'function changeThreshold(uint256 _threshold) external', | ||
'function execTransaction(address to, uint256 value, bytes calldata data, uint8 operation, uint256 safeTxGas, uint256 baseGas, uint256 gasPrice, address gasToken, address refundReceiver, bytes calldata signatures) external returns (bool success)', | ||
'function nonce() view returns (uint256)', | ||
'function getOwners() view returns (address[])', | ||
] | ||
|
||
// Create contract instance | ||
const gnosisSafeContract = new ethers.Contract( | ||
args.address, | ||
gnosisSafeAbi, | ||
signer | ||
) | ||
console.log('Gnosis Safe Contract:', gnosisSafeContract.address) | ||
|
||
// Execute | ||
try { | ||
// Add 2 owners to Safe | ||
await addOwnerAndVerify(gnosisSafeContract, owners[0], 1, [signer]) | ||
await addOwnerAndVerify(gnosisSafeContract, owners[1], 1, [signer]) | ||
|
||
// Change threshold to 3 | ||
const tx3 = await executeContractCallWithSigners( | ||
gnosisSafeContract, | ||
gnosisSafeContract, | ||
'changeThreshold', | ||
[3], | ||
[signer] | ||
) | ||
console.log('Tx 3 Hash:', tx3.hash) | ||
await tx3.wait() | ||
const newSafeThreshold = await gnosisSafeContract.getThreshold() | ||
console.log('New threshold:', String(newSafeThreshold)) | ||
} catch (error) { | ||
console.error('Got the error running Safe contract:', error) | ||
} | ||
}) |