@tempus-labs/utils
Version:
Tempus utility helpers
165 lines • 6.74 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.expectRevert = exports.getRevertMessage = exports.impersonateAccount = exports.setBalanceOf = exports.setStorageField = exports.setStorageAtAddr = exports.setNextBlockTimestamp = exports.setEvmTime = exports.increaseTime = exports.evmMineInSingleBlock = exports.evmSetAutomine = exports.evmMine = exports.blockTimestamp = void 0;
const hardhat_1 = require("hardhat");
const chai_1 = require("chai");
const ContractBase_1 = require("./ContractBase");
/**
* @returns Latest timestamp of the blockchain
*/
async function blockTimestamp() {
return (await hardhat_1.ethers.provider.getBlock('latest')).timestamp;
}
exports.blockTimestamp = blockTimestamp;
/**
* Mines a block
*/
async function evmMine() {
await hardhat_1.ethers.provider.send("evm_mine", []);
}
exports.evmMine = evmMine;
/**
* sets the current EVM automining behavior.
* if true - a block is mined for every sent transaction (default is true)
*/
async function evmSetAutomine(automine) {
await hardhat_1.ethers.provider.send("evm_setAutomine", [automine]);
}
exports.evmSetAutomine = evmSetAutomine;
/**
* Disables Automine until the block `fn` is finished executing.
* Then manually processes all pending transactions.
*/
async function evmMineInSingleBlock(fn) {
await evmSetAutomine(false);
await fn();
await evmMine();
await evmSetAutomine(true);
}
exports.evmMineInSingleBlock = evmMineInSingleBlock;
/**
* Increase current EVM time by seconds
*/
async function increaseTime(addSeconds) {
await hardhat_1.ethers.provider.send("evm_increaseTime", [addSeconds]);
await evmMine();
}
exports.increaseTime = increaseTime;
/**
* Set current EVM time
*/
async function setEvmTime(timestamp) {
await setNextBlockTimestamp(timestamp);
await evmMine();
}
exports.setEvmTime = setEvmTime;
/**
* Set The timestamp of the next block (without mining it)
*/
async function setNextBlockTimestamp(timestamp) {
await hardhat_1.ethers.provider.send("evm_setNextBlockTimestamp", [timestamp]);
}
exports.setNextBlockTimestamp = setNextBlockTimestamp;
/**
* Overwrites storage at address with a new value
*/
async function setStorageAtAddr(contract, addr, value) {
const val = value.toHexString();
return hardhat_1.ethers.provider.send('hardhat_setStorageAt', [contract.address, addr, val]);
}
exports.setStorageAtAddr = setStorageAtAddr;
/**
* Overwrites storage by full field name, eg "lido.Lido.beaconBalance" with a new value
*/
async function setStorageField(contract, fieldName, value) {
const addr = hardhat_1.ethers.utils.keccak256(hardhat_1.ethers.utils.toUtf8Bytes(fieldName)).replace("0x0", "0x");
return setStorageAtAddr(contract, addr, value);
}
exports.setStorageField = setStorageField;
/**
* Modifies the ETH Balance of an account
*/
async function setBalanceOf(address, ethAmount) {
await hardhat_1.network.provider.send("hardhat_setBalance", [(0, ContractBase_1.addressOf)(address), ethAmount.toHexString()]);
}
exports.setBalanceOf = setBalanceOf;
/**
* Impersonates an account and creates a Signer from it
* @returns A new Signer that impersonates account from `address`
*/
async function impersonateAccount(address) {
await hardhat_1.network.provider.request({ method: "hardhat_impersonateAccount", params: [(0, ContractBase_1.addressOf)(address)] });
return hardhat_1.ethers.getSigner((0, ContractBase_1.addressOf)(address));
}
exports.impersonateAccount = impersonateAccount;
function parseRevertMessage(msg) {
if (msg.indexOf("VM Exception while processing transaction:") !== -1) {
const error1 = "VM Exception while processing transaction: reverted with reason string '";
let idx = msg.indexOf(error1);
if (idx !== -1) {
// ex: "VM Exception while processing transaction: reverted with reason string 'Receiver cannot be 0.'"
// returns: "Receiver cannot be 0."
return msg.slice(idx + error1.length, msg.length - 1);
}
const error2 = "VM Exception while processing transaction: reverted with custom error '";
idx = msg.indexOf(error2);
if (idx !== -1) {
// ex: "VM Exception while processing transaction: reverted with custom error 'OnlyControllerAuthorized("0x81aBfd14a19131A347a557A6c5757e7f71910E73")'"
// returns: ":SenderIsNotStaker"
const customError = msg.slice(idx + error2.length, msg.length - 1);
// discard the "(arg0, arg1, ...)" part if it's present
const parentheses = customError.indexOf('(');
return ":" + (parentheses !== -1 ? customError.substring(0, parentheses) : customError);
}
// old style where ": revert " was directly followed by error string
const error3 = "VM Exception while processing transaction: revert ";
idx = msg.indexOf(error3);
if (idx !== -1) {
return msg.slice(idx + error3.length);
}
const error4 = "VM Exception while processing transaction: reverted";
idx = msg.indexOf(error4);
if (idx !== -1) {
return msg.slice(idx + error4.length - 8);
}
throw new Error(`Unrecognized VM Exception format: "${msg}"`);
}
// most likely a raw error or revert string
return msg;
}
/**
* Tries to get the Revert Message from an Error
*/
function getRevertMessage(e) {
// always prefer e.reason if it's present, because some
if (e.reason) {
// ex: "VM Exception while processing transaction: reverted with reason string 'reverted'"
// or: "No vesting data for receiver."
return parseRevertMessage(e.reason);
}
if (e.errorName) {
// errorName: "InvalidTokenIn"
return ":" + e.errorName; // for custom errors we use ":" prefix
}
// this is usually an Error object with revert message in Error.message property
return parseRevertMessage(e.message);
}
exports.getRevertMessage = getRevertMessage;
/**
* Expect called promise to revert with message
* (await expectRevert(lido.withdraw(..))).to.equal("expected revert msg");
*
* We use this helper instead of `.to.be.revertedWith`, because that doesn't allow showing a stack trace,
* and this one does.
*/
async function expectRevert(promise) {
try {
await promise;
return (0, chai_1.expect)('success');
}
catch (e) {
return (0, chai_1.expect)(getRevertMessage(e));
}
}
exports.expectRevert = expectRevert;
//# sourceMappingURL=Utils.js.map