Created
February 25, 2025 07:19
-
-
Save TrejGun/26420487df5fe5728e83759eb186893e to your computer and use it in GitHub Desktop.
multisig
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
import { OperationType } from "@safe-global/types-kit"; | |
import Safe from "@safe-global/protocol-kit"; | |
import SafeApiKit from "@safe-global/api-kit"; | |
import { encodeFunctionData, Hash } from "viem"; | |
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"; | |
import { sepolia } from "viem/chains"; | |
import { waitForTransactionReceipt } from "viem/actions"; | |
const ERC20_TOKEN_ADDRESS = "0x..."; | |
const AMOUNT = 100n ** 18n; | |
await (async () => { | |
const deployerPrivateKey = generatePrivateKey(); | |
const { address: _deployerAccount } = privateKeyToAccount(deployerPrivateKey); | |
const adminPrivateKey1 = generatePrivateKey(); | |
const { address: adminAccount1 } = privateKeyToAccount(adminPrivateKey1); | |
const adminPrivateKey2 = generatePrivateKey(); | |
const { address: adminAccount2 } = privateKeyToAccount(adminPrivateKey2); | |
const senderPrivateKey = generatePrivateKey(); | |
const { address: senderAccount } = privateKeyToAccount(senderPrivateKey); | |
const receiverPrivateKey = generatePrivateKey(); | |
const { address: receiverAccount } = privateKeyToAccount(receiverPrivateKey); | |
const protocolKitDeployer = await Safe.init({ | |
provider: sepolia.rpcUrls.default.http[0], | |
signer: deployerPrivateKey, | |
predictedSafe: { | |
safeAccountConfig: { | |
owners: [senderAccount, adminAccount1, adminAccount2], | |
threshold: 2, | |
}, | |
}, | |
}); | |
const predictedSafeAddress = await protocolKitDeployer.getAddress(); | |
const deploymentTransaction = await protocolKitDeployer.createSafeDeploymentTransaction(); | |
const client = await protocolKitDeployer.getSafeProvider().getExternalSigner(); | |
if (!client) { | |
throw new Error(); | |
} | |
const transactionHash = await client.sendTransaction({ | |
to: deploymentTransaction.to, | |
value: BigInt(deploymentTransaction.value), | |
data: deploymentTransaction.data as Hash, | |
chain: sepolia, | |
}); | |
await waitForTransactionReceipt(client, { hash: transactionHash }); | |
const newProtocolKit = await protocolKitDeployer.connect({ | |
safeAddress: predictedSafeAddress, | |
}); | |
const isSafeDeployed = await newProtocolKit.isSafeDeployed(); | |
if (!isSafeDeployed) { | |
throw new Error("Wallet was not deployed"); | |
} | |
const actualSafeAddress = await newProtocolKit.getAddress(); | |
if (predictedSafeAddress !== actualSafeAddress) { | |
throw new Error("Address mismatch"); | |
} | |
const protocolKitOwner = await Safe.init({ | |
provider: sepolia.rpcUrls.default.http[0], | |
signer: senderPrivateKey, | |
safeAddress: actualSafeAddress, | |
}); | |
const safeTransactionData = { | |
to: ERC20_TOKEN_ADDRESS, | |
value: "0", | |
data: encodeFunctionData({ | |
abi: [ | |
{ | |
name: "transferFrom", | |
type: "function", | |
stateMutability: "nonpayable", | |
inputs: [ | |
{ name: "from", type: "address" }, | |
{ name: "to", type: "address" }, | |
{ name: "amount", type: "uint256" }, | |
], | |
outputs: [], | |
}, | |
], | |
functionName: "transferFrom", | |
args: [senderAccount, receiverAccount, AMOUNT], | |
}), | |
operation: OperationType.Call, | |
}; | |
const safeTransaction = await protocolKitOwner.createTransaction({ | |
transactions: [safeTransactionData], | |
}); | |
const safeTransactionHash = await protocolKitOwner.getTransactionHash(safeTransaction); | |
const senderSignature = await protocolKitOwner.signHash(safeTransactionHash); | |
const apiKit = new SafeApiKit({ | |
chainId: BigInt(sepolia.id), | |
}); | |
await apiKit.proposeTransaction({ | |
safeAddress: actualSafeAddress, | |
safeTransactionData: safeTransaction.data, | |
safeTxHash: safeTransactionHash, | |
senderAddress: senderAccount, | |
senderSignature: senderSignature.data, | |
}); | |
const protocolKitAdmin = await Safe.init({ | |
provider: sepolia.rpcUrls.default.http[0], | |
signer: adminPrivateKey1, | |
safeAddress: actualSafeAddress, | |
}); | |
const signature = await protocolKitAdmin.signHash(safeTransactionHash); | |
await apiKit.confirmTransaction(safeTransactionHash, signature.data); | |
const executableTransaction = await apiKit.getTransaction(safeTransactionHash); | |
await protocolKitOwner.executeTransaction(executableTransaction); | |
})() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment