@safe-global/safe-contracts
Version:
Ethereum multisig contract
203 lines (173 loc) • 7.42 kB
text/typescript
import hre, { deployments } from "hardhat";
import { Wallet, Contract } from "ethers";
import { AddressZero } from "@ethersproject/constants";
import solc from "solc";
import { logGas } from "../../src/utils/execution";
import { safeContractUnderTest } from "./config";
import { getRandomIntAsString } from "./numbers";
export const defaultTokenCallbackHandlerDeployment = async () => {
return await deployments.get("TokenCallbackHandler");
};
export const defaultTokenCallbackHandlerContract = async () => {
return await hre.ethers.getContractFactory("TokenCallbackHandler");
};
export const compatFallbackHandlerDeployment = async () => {
return await deployments.get("CompatibilityFallbackHandler");
};
export const compatFallbackHandlerContract = async () => {
return await hre.ethers.getContractFactory("CompatibilityFallbackHandler");
};
export const getSafeSingleton = async () => {
const SafeDeployment = await deployments.get(safeContractUnderTest());
const Safe = await hre.ethers.getContractFactory(safeContractUnderTest());
return Safe.attach(SafeDeployment.address);
};
export const getSafeSingletonContract = async () => {
const safeSingletonDeployment = await deployments.get("Safe");
const safe = await hre.ethers.getContractAt("Safe", safeSingletonDeployment.address);
return safe;
};
export const getSafeL2SingletonContract = async () => {
const safeSingletonDeployment = await deployments.get("SafeL2");
const Safe = await hre.ethers.getContractAt("SafeL2", safeSingletonDeployment.address);
return Safe;
};
export const safeMigrationContract = async () => {
const SafeMigrationDeployment = await deployments.get("SafeMigration");
const SafeMigration = await hre.ethers.getContractAt("SafeMigration", SafeMigrationDeployment.address);
return SafeMigration;
};
export const getSafeSingletonAt = async (address: string) => {
const safe = await hre.ethers.getContractAt(safeContractUnderTest(), address);
return safe;
};
export const getSafeWithSingleton = async (
singleton: Contract,
owners: string[],
threshold?: number,
fallbackHandler?: string,
saltNumber: string = getRandomIntAsString(),
) => {
const factory = await getFactory();
const singletonAddress = singleton.address;
const template = await factory.callStatic.createProxyWithNonce(singletonAddress, "0x", saltNumber);
await factory.createProxyWithNonce(singletonAddress, "0x", saltNumber).then((tx: any) => tx.wait());
const safeProxy = singleton.attach(template);
await safeProxy.setup(
owners,
threshold || owners.length,
AddressZero,
"0x",
fallbackHandler || AddressZero,
AddressZero,
0,
AddressZero,
);
return safeProxy;
};
export const getFactoryContract = async () => {
const factory = await hre.ethers.getContractFactory("SafeProxyFactory");
return factory;
};
export const getFactory = async () => {
const FactoryDeployment = await deployments.get("SafeProxyFactory");
const Factory = await hre.ethers.getContractFactory("SafeProxyFactory");
return Factory.attach(FactoryDeployment.address);
};
export const getSimulateTxAccessor = async () => {
const SimulateTxAccessorDeployment = await deployments.get("SimulateTxAccessor");
const SimulateTxAccessor = await hre.ethers.getContractFactory("SimulateTxAccessor");
return SimulateTxAccessor.attach(SimulateTxAccessorDeployment.address);
};
export const getMultiSend = async () => {
const MultiSendDeployment = await deployments.get("MultiSend");
const MultiSend = await hre.ethers.getContractFactory("MultiSend");
return MultiSend.attach(MultiSendDeployment.address);
};
export const getMultiSendCallOnly = async () => {
const MultiSendDeployment = await deployments.get("MultiSendCallOnly");
const MultiSend = await hre.ethers.getContractFactory("MultiSendCallOnly");
return MultiSend.attach(MultiSendDeployment.address);
};
export const getCreateCall = async () => {
const CreateCallDeployment = await deployments.get("CreateCall");
const CreateCall = await hre.ethers.getContractFactory("CreateCall");
return CreateCall.attach(CreateCallDeployment.address);
};
export const migrationContract = async () => {
return await hre.ethers.getContractFactory("Migration");
};
export const getMock = async () => {
const Mock = await hre.ethers.getContractFactory("MockContract");
return await Mock.deploy();
};
export const getSafeTemplate = async (saltNumber: string = getRandomIntAsString()) => {
const singleton = await getSafeSingleton();
const factory = await getFactory();
const template = await factory.callStatic.createProxyWithNonce(singleton.address, "0x", saltNumber);
await factory.createProxyWithNonce(singleton.address, "0x", saltNumber).then((tx: any) => tx.wait());
const Safe = await hre.ethers.getContractFactory(safeContractUnderTest());
return Safe.attach(template);
};
export const getSafeWithOwners = async (
owners: string[],
threshold?: number,
fallbackHandler?: string,
logGasUsage?: boolean,
saltNumber: string = getRandomIntAsString(),
) => {
const template = await getSafeTemplate(saltNumber);
await logGas(
`Setup Safe with ${owners.length} owner(s)${fallbackHandler && fallbackHandler !== AddressZero ? " and fallback handler" : ""}`,
template.setup(owners, threshold || owners.length, AddressZero, "0x", fallbackHandler || AddressZero, AddressZero, 0, AddressZero),
!logGasUsage,
);
return template;
};
export const getTokenCallbackHandler = async () => {
return (await defaultTokenCallbackHandlerContract()).attach((await defaultTokenCallbackHandlerDeployment()).address);
};
export const getCompatFallbackHandler = async () => {
return (await compatFallbackHandlerContract()).attach((await compatFallbackHandlerDeployment()).address);
};
export const getSafeProxyRuntimeCode = async () => {
const proxyArtifact = await hre.artifacts.readArtifact("SafeProxy");
return proxyArtifact.deployedBytecode;
};
export const compile = async (source: string) => {
const input = JSON.stringify({
language: "Solidity",
settings: {
outputSelection: {
"*": {
"*": ["abi", "evm.bytecode"],
},
},
},
sources: {
"tmp.sol": {
content: source,
},
},
});
const solcData = await solc.compile(input);
const output = JSON.parse(solcData);
if (!output["contracts"]) {
console.log(output);
throw Error("Could not compile contract");
}
const fileOutput = output["contracts"]["tmp.sol"];
const contractOutput = fileOutput[Object.keys(fileOutput)[0]];
const abi = contractOutput["abi"];
const data = "0x" + contractOutput["evm"]["bytecode"]["object"];
return {
data: data,
interface: abi,
};
};
export const deployContract = async (deployer: Wallet, source: string): Promise<Contract> => {
const output = await compile(source);
const transaction = await deployer.sendTransaction({ data: output.data, gasLimit: 6000000 });
const receipt = await transaction.wait();
return new Contract(receipt.contractAddress, output.interface, deployer);
};