mirror of
https://github.com/Instadapp/aave-protocol-v2.git
synced 2024-07-29 21:47:30 +00:00
Added depositWithPermit, repayWithPermit test scenarios
This commit is contained in:
parent
6810940c9f
commit
3a6948ce2c
|
@ -8,14 +8,67 @@ import {ERC20} from '../../dependencies/openzeppelin/contracts/ERC20.sol';
|
|||
* @dev ERC20 minting logic
|
||||
*/
|
||||
contract MintableERC20 is ERC20 {
|
||||
|
||||
bytes public constant EIP712_REVISION = bytes('1');
|
||||
bytes32 internal constant EIP712_DOMAIN =
|
||||
keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)');
|
||||
bytes32 public constant PERMIT_TYPEHASH =
|
||||
keccak256('Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)');
|
||||
|
||||
mapping(address => uint256) public _nonces;
|
||||
|
||||
bytes32 public DOMAIN_SEPARATOR;
|
||||
|
||||
constructor(
|
||||
string memory name,
|
||||
string memory symbol,
|
||||
uint8 decimals
|
||||
) public ERC20(name, symbol) {
|
||||
|
||||
uint256 chainId;
|
||||
|
||||
assembly {
|
||||
chainId := chainid()
|
||||
}
|
||||
|
||||
DOMAIN_SEPARATOR = keccak256(
|
||||
abi.encode(
|
||||
EIP712_DOMAIN,
|
||||
keccak256(bytes(name)),
|
||||
keccak256(EIP712_REVISION),
|
||||
chainId,
|
||||
address(this)
|
||||
)
|
||||
);
|
||||
_setupDecimals(decimals);
|
||||
}
|
||||
|
||||
function permit(
|
||||
address owner,
|
||||
address spender,
|
||||
uint256 value,
|
||||
uint256 deadline,
|
||||
uint8 v,
|
||||
bytes32 r,
|
||||
bytes32 s
|
||||
) external {
|
||||
require(owner != address(0), 'INVALID_OWNER');
|
||||
//solium-disable-next-line
|
||||
require(block.timestamp <= deadline, 'INVALID_EXPIRATION');
|
||||
uint256 currentValidNonce = _nonces[owner];
|
||||
bytes32 digest =
|
||||
keccak256(
|
||||
abi.encodePacked(
|
||||
'\x19\x01',
|
||||
DOMAIN_SEPARATOR,
|
||||
keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, currentValidNonce, deadline))
|
||||
)
|
||||
);
|
||||
require(owner == ecrecover(digest, v, r, s), 'INVALID_SIGNATURE');
|
||||
_nonces[owner] = currentValidNonce.add(1);
|
||||
_approve(owner, spender, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Function to mint tokens
|
||||
* @param value The amount of tokens to mint.
|
||||
|
|
|
@ -363,3 +363,5 @@ export const getFlashLiquidationAdapter = async (address?: tEthereumAddress) =>
|
|||
.address,
|
||||
await getFirstSigner()
|
||||
);
|
||||
|
||||
export const getChainId = async () => (await DRE.ethers.provider.getNetwork()).chainId;
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
"test": "TS_NODE_TRANSPILE_ONLY=1 hardhat test ./test-suites/test-aave/*.spec.ts",
|
||||
"test-amm": "TS_NODE_TRANSPILE_ONLY=1 hardhat test ./test-suites/test-amm/*.spec.ts",
|
||||
"test-amm-scenarios": "TS_NODE_TRANSPILE_ONLY=1 hardhat test ./test-suites/test-amm/__setup.spec.ts test-suites/test-amm/scenario.spec.ts",
|
||||
"test-scenarios": "npx hardhat test test-suites/test-aave/__setup.spec.ts test-suites/test-aave/scenario.spec.ts",
|
||||
"test-scenarios": "hardhat test test-suites/test-aave/__setup.spec.ts test-suites/test-aave/scenario.spec.ts",
|
||||
"test-repay-with-collateral": "hardhat test test-suites/test-aave/__setup.spec.ts test-suites/test-aave/repay-with-collateral.spec.ts",
|
||||
"test-liquidate-with-collateral": "hardhat test test-suites/test-aave/__setup.spec.ts test-suites/test-aave/flash-liquidation-with-collateral.spec.ts",
|
||||
"test-liquidate-underlying": "hardhat test test-suites/test-aave/__setup.spec.ts test-suites/test-aave/liquidation-underlying.spec.ts",
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
calcExpectedUserDataAfterWithdraw,
|
||||
} from './utils/calculations';
|
||||
import { getReserveAddressFromSymbol, getReserveData, getUserData } from './utils/helpers';
|
||||
import { buildPermitParams, getSignatureFromTypedData } from '../../../helpers/contracts-helpers';
|
||||
|
||||
import { convertToCurrencyDecimals } from '../../../helpers/contracts-helpers';
|
||||
import {
|
||||
|
@ -23,6 +24,7 @@ import {
|
|||
getMintableERC20,
|
||||
getStableDebtToken,
|
||||
getVariableDebtToken,
|
||||
getChainId,
|
||||
} from '../../../helpers/contracts-getters';
|
||||
import { MAX_UINT_AMOUNT, ONE_YEAR } from '../../../helpers/constants';
|
||||
import { SignerWithAddress, TestEnv } from './make-suite';
|
||||
|
@ -30,9 +32,10 @@ import { advanceTimeAndBlock, DRE, timeLatest, waitForTx } from '../../../helper
|
|||
|
||||
import chai from 'chai';
|
||||
import { ReserveData, UserReserveData } from './utils/interfaces';
|
||||
import { ContractReceipt } from 'ethers';
|
||||
import { ContractReceipt, Wallet } from 'ethers';
|
||||
import { AToken } from '../../../types/AToken';
|
||||
import { RateMode, tEthereumAddress } from '../../../helpers/types';
|
||||
import { MintableERC20Factory } from '../../../types';
|
||||
|
||||
const { expect } = chai;
|
||||
|
||||
|
@ -349,7 +352,7 @@ export const borrow = async (
|
|||
);
|
||||
|
||||
const amountToBorrow = await convertToCurrencyDecimals(reserve, amount);
|
||||
|
||||
|
||||
if (expectedResult === 'success') {
|
||||
const txResult = await waitForTx(
|
||||
await pool
|
||||
|
@ -515,6 +518,257 @@ export const repay = async (
|
|||
}
|
||||
};
|
||||
|
||||
export const depositWithPermit = async (
|
||||
reserveSymbol: string,
|
||||
amount: string,
|
||||
sender: SignerWithAddress,
|
||||
senderPk: string,
|
||||
onBehalfOf: tEthereumAddress,
|
||||
sendValue: string,
|
||||
expectedResult: string,
|
||||
testEnv: TestEnv,
|
||||
revertMessage?: string
|
||||
) => {
|
||||
const { pool } = testEnv;
|
||||
|
||||
const reserve = await getReserveAddressFromSymbol(reserveSymbol);
|
||||
const amountToDeposit = await convertToCurrencyDecimals(reserve, amount);
|
||||
|
||||
const chainId = await getChainId();
|
||||
const token = new MintableERC20Factory(sender.signer).attach(reserve);
|
||||
const highDeadline = '100000000000000000000000000';
|
||||
const nonce = await token._nonces(sender.address);
|
||||
|
||||
const msgParams = buildPermitParams(
|
||||
chainId,
|
||||
reserve,
|
||||
'1',
|
||||
reserveSymbol,
|
||||
sender.address,
|
||||
pool.address,
|
||||
nonce.toNumber(),
|
||||
highDeadline,
|
||||
amountToDeposit.toString()
|
||||
);
|
||||
const { v, r, s } = getSignatureFromTypedData(senderPk, msgParams);
|
||||
|
||||
const txOptions: any = {};
|
||||
|
||||
const { reserveData: reserveDataBefore, userData: userDataBefore } = await getContractsData(
|
||||
reserve,
|
||||
onBehalfOf,
|
||||
testEnv,
|
||||
sender.address
|
||||
);
|
||||
|
||||
if (sendValue) {
|
||||
txOptions.value = await convertToCurrencyDecimals(reserve, sendValue);
|
||||
}
|
||||
|
||||
if (expectedResult === 'success') {
|
||||
const txResult = await waitForTx(
|
||||
await pool
|
||||
.connect(sender.signer)
|
||||
.depositWithPermit(
|
||||
reserve,
|
||||
amountToDeposit,
|
||||
onBehalfOf,
|
||||
'0',
|
||||
highDeadline,
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
txOptions
|
||||
)
|
||||
);
|
||||
|
||||
const {
|
||||
reserveData: reserveDataAfter,
|
||||
userData: userDataAfter,
|
||||
timestamp,
|
||||
} = await getContractsData(reserve, onBehalfOf, testEnv, sender.address);
|
||||
|
||||
const { txCost, txTimestamp } = await getTxCostAndTimestamp(txResult);
|
||||
|
||||
const expectedReserveData = calcExpectedReserveDataAfterDeposit(
|
||||
amountToDeposit.toString(),
|
||||
reserveDataBefore,
|
||||
txTimestamp
|
||||
);
|
||||
|
||||
const expectedUserReserveData = calcExpectedUserDataAfterDeposit(
|
||||
amountToDeposit.toString(),
|
||||
reserveDataBefore,
|
||||
expectedReserveData,
|
||||
userDataBefore,
|
||||
txTimestamp,
|
||||
timestamp,
|
||||
txCost
|
||||
);
|
||||
|
||||
expectEqual(reserveDataAfter, expectedReserveData);
|
||||
expectEqual(userDataAfter, expectedUserReserveData);
|
||||
|
||||
// truffleAssert.eventEmitted(txResult, "Deposit", (ev: any) => {
|
||||
// const {_reserve, _user, _amount} = ev;
|
||||
// return (
|
||||
// _reserve === reserve &&
|
||||
// _user === user &&
|
||||
// new BigNumber(_amount).isEqualTo(new BigNumber(amountToDeposit))
|
||||
// );
|
||||
// });
|
||||
} else if (expectedResult === 'revert') {
|
||||
await expect(
|
||||
pool
|
||||
.connect(sender.signer)
|
||||
.depositWithPermit(
|
||||
reserve,
|
||||
amountToDeposit,
|
||||
onBehalfOf,
|
||||
'0',
|
||||
highDeadline,
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
txOptions
|
||||
),
|
||||
revertMessage
|
||||
).to.be.reverted;
|
||||
}
|
||||
};
|
||||
|
||||
export const repayWithPermit = async (
|
||||
reserveSymbol: string,
|
||||
amount: string,
|
||||
rateMode: string,
|
||||
user: SignerWithAddress,
|
||||
userPk: string,
|
||||
onBehalfOf: SignerWithAddress,
|
||||
sendValue: string,
|
||||
expectedResult: string,
|
||||
testEnv: TestEnv,
|
||||
revertMessage?: string
|
||||
) => {
|
||||
const { pool } = testEnv;
|
||||
const reserve = await getReserveAddressFromSymbol(reserveSymbol);
|
||||
const highDeadline = '100000000000000000000000000';
|
||||
|
||||
const { reserveData: reserveDataBefore, userData: userDataBefore } = await getContractsData(
|
||||
reserve,
|
||||
onBehalfOf.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
let amountToRepay = '0';
|
||||
|
||||
if (amount !== '-1') {
|
||||
amountToRepay = (await convertToCurrencyDecimals(reserve, amount)).toString();
|
||||
} else {
|
||||
amountToRepay = MAX_UINT_AMOUNT;
|
||||
}
|
||||
amountToRepay = '0x' + new BigNumber(amountToRepay).toString(16);
|
||||
|
||||
const chainId = await getChainId();
|
||||
const token = new MintableERC20Factory(user.signer).attach(reserve);
|
||||
const nonce = await token._nonces(user.address);
|
||||
|
||||
const msgParams = buildPermitParams(
|
||||
chainId,
|
||||
reserve,
|
||||
'1',
|
||||
reserveSymbol,
|
||||
user.address,
|
||||
pool.address,
|
||||
nonce.toNumber(),
|
||||
highDeadline,
|
||||
amountToRepay
|
||||
);
|
||||
const { v, r, s } = getSignatureFromTypedData(userPk, msgParams);
|
||||
const txOptions: any = {};
|
||||
|
||||
if (sendValue) {
|
||||
const valueToSend = await convertToCurrencyDecimals(reserve, sendValue);
|
||||
txOptions.value = '0x' + new BigNumber(valueToSend.toString()).toString(16);
|
||||
}
|
||||
|
||||
if (expectedResult === 'success') {
|
||||
const txResult = await waitForTx(
|
||||
await pool
|
||||
.connect(user.signer)
|
||||
.repayWithPermit(
|
||||
reserve,
|
||||
amountToRepay,
|
||||
rateMode,
|
||||
onBehalfOf.address,
|
||||
highDeadline,
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
txOptions
|
||||
)
|
||||
);
|
||||
|
||||
const { txCost, txTimestamp } = await getTxCostAndTimestamp(txResult);
|
||||
|
||||
const {
|
||||
reserveData: reserveDataAfter,
|
||||
userData: userDataAfter,
|
||||
timestamp,
|
||||
} = await getContractsData(reserve, onBehalfOf.address, testEnv);
|
||||
|
||||
const expectedReserveData = calcExpectedReserveDataAfterRepay(
|
||||
amountToRepay,
|
||||
<RateMode>rateMode,
|
||||
reserveDataBefore,
|
||||
userDataBefore,
|
||||
txTimestamp,
|
||||
timestamp
|
||||
);
|
||||
|
||||
const expectedUserData = calcExpectedUserDataAfterRepay(
|
||||
amountToRepay,
|
||||
<RateMode>rateMode,
|
||||
reserveDataBefore,
|
||||
expectedReserveData,
|
||||
userDataBefore,
|
||||
user.address,
|
||||
onBehalfOf.address,
|
||||
txTimestamp,
|
||||
timestamp
|
||||
);
|
||||
|
||||
expectEqual(reserveDataAfter, expectedReserveData);
|
||||
expectEqual(userDataAfter, expectedUserData);
|
||||
|
||||
// truffleAssert.eventEmitted(txResult, "Repay", (ev: any) => {
|
||||
// const {_reserve, _user, _repayer} = ev;
|
||||
|
||||
// return (
|
||||
// _reserve.toLowerCase() === reserve.toLowerCase() &&
|
||||
// _user.toLowerCase() === onBehalfOf.toLowerCase() &&
|
||||
// _repayer.toLowerCase() === user.toLowerCase()
|
||||
// );
|
||||
// });
|
||||
} else if (expectedResult === 'revert') {
|
||||
await expect(
|
||||
pool
|
||||
.connect(user.signer)
|
||||
.repayWithPermit(
|
||||
reserve,
|
||||
amountToRepay,
|
||||
rateMode,
|
||||
onBehalfOf.address,
|
||||
highDeadline,
|
||||
v,
|
||||
r,
|
||||
s,
|
||||
txOptions
|
||||
),
|
||||
revertMessage
|
||||
).to.be.reverted;
|
||||
}
|
||||
};
|
||||
|
||||
export const setUseAsCollateral = async (
|
||||
reserveSymbol: string,
|
||||
user: SignerWithAddress,
|
||||
|
|
|
@ -10,8 +10,11 @@ import {
|
|||
swapBorrowRateMode,
|
||||
rebalanceStableBorrowRate,
|
||||
delegateBorrowAllowance,
|
||||
repayWithPermit,
|
||||
depositWithPermit,
|
||||
} from './actions';
|
||||
import { RateMode } from '../../../helpers/types';
|
||||
import { Wallet } from '@ethersproject/wallet';
|
||||
|
||||
export interface Action {
|
||||
name: string;
|
||||
|
@ -73,6 +76,12 @@ const executeAction = async (action: Action, users: SignerWithAddress[], testEnv
|
|||
|
||||
const user = users[parseInt(userIndex)];
|
||||
|
||||
const userPrivateKey = require('../../../test-wallets.js').accounts[parseInt(userIndex) + 1]
|
||||
.secretKey;
|
||||
if (!userPrivateKey) {
|
||||
throw new Error('INVALID_OWNER_PK');
|
||||
}
|
||||
|
||||
switch (name) {
|
||||
case 'mint':
|
||||
const { amount } = action.args;
|
||||
|
@ -111,6 +120,30 @@ const executeAction = async (action: Action, users: SignerWithAddress[], testEnv
|
|||
);
|
||||
}
|
||||
break;
|
||||
case 'depositWithPermit':
|
||||
{
|
||||
const { amount, sendValue, onBehalfOf: onBehalfOfIndex } = action.args;
|
||||
const onBehalfOf = onBehalfOfIndex
|
||||
? users[parseInt(onBehalfOfIndex)].address
|
||||
: user.address;
|
||||
|
||||
if (!amount || amount === '') {
|
||||
throw `Invalid amount to deposit into the ${reserve} reserve`;
|
||||
}
|
||||
|
||||
await depositWithPermit(
|
||||
reserve,
|
||||
amount,
|
||||
user,
|
||||
userPrivateKey,
|
||||
onBehalfOf,
|
||||
sendValue,
|
||||
expected,
|
||||
testEnv,
|
||||
revertMessage
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'delegateBorrowAllowance':
|
||||
{
|
||||
|
@ -203,6 +236,40 @@ const executeAction = async (action: Action, users: SignerWithAddress[], testEnv
|
|||
}
|
||||
break;
|
||||
|
||||
case 'repayWithPermit':
|
||||
{
|
||||
const { amount, borrowRateMode, sendValue, deadline } = action.args;
|
||||
let { onBehalfOf: onBehalfOfIndex } = action.args;
|
||||
|
||||
if (!amount || amount === '') {
|
||||
throw `Invalid amount to repay into the ${reserve} reserve`;
|
||||
}
|
||||
|
||||
let userToRepayOnBehalf: SignerWithAddress;
|
||||
if (!onBehalfOfIndex || onBehalfOfIndex === '') {
|
||||
console.log(
|
||||
'WARNING: No onBehalfOf specified for a repay action. Defaulting to the repayer address'
|
||||
);
|
||||
userToRepayOnBehalf = user;
|
||||
} else {
|
||||
userToRepayOnBehalf = users[parseInt(onBehalfOfIndex)];
|
||||
}
|
||||
|
||||
await repayWithPermit(
|
||||
reserve,
|
||||
amount,
|
||||
rateMode,
|
||||
user,
|
||||
userPrivateKey,
|
||||
userToRepayOnBehalf,
|
||||
sendValue,
|
||||
expected,
|
||||
testEnv,
|
||||
revertMessage
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'setUseAsCollateral':
|
||||
{
|
||||
const { useAsCollateral } = action.args;
|
||||
|
|
|
@ -0,0 +1,191 @@
|
|||
{
|
||||
"title": "LendingPool: Borrow/repay with permit with Permit (variable rate)",
|
||||
"description": "Test cases for the borrow function, variable mode.",
|
||||
"stories": [
|
||||
{
|
||||
"description": "User 2 deposits with permit 1 DAI to account for rounding errors",
|
||||
"actions": [
|
||||
{
|
||||
"name": "mint",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"amount": "1",
|
||||
"user": "2"
|
||||
},
|
||||
"expected": "success"
|
||||
},
|
||||
{
|
||||
"name": "depositWithPermit",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"amount": "1",
|
||||
"user": "2"
|
||||
},
|
||||
"expected": "success"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "User 0 deposits with permit 1000 DAI, user 1 deposits 1 WETH as collateral and borrows 100 DAI at variable rate",
|
||||
"actions": [
|
||||
{
|
||||
"name": "mint",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"amount": "1000",
|
||||
"user": "0"
|
||||
},
|
||||
"expected": "success"
|
||||
},
|
||||
{
|
||||
"name": "depositWithPermit",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"amount": "1000",
|
||||
"user": "0"
|
||||
},
|
||||
"expected": "success"
|
||||
},
|
||||
{
|
||||
"name": "mint",
|
||||
"args": {
|
||||
"reserve": "WETH",
|
||||
"amount": "1",
|
||||
"user": "1"
|
||||
},
|
||||
"expected": "success"
|
||||
},
|
||||
{
|
||||
"name": "approve",
|
||||
"args": {
|
||||
"reserve": "WETH",
|
||||
"user": "1"
|
||||
},
|
||||
"expected": "success"
|
||||
},
|
||||
{
|
||||
"name": "deposit",
|
||||
"args": {
|
||||
"reserve": "WETH",
|
||||
"amount": "1",
|
||||
"user": "1"
|
||||
},
|
||||
"expected": "success"
|
||||
},
|
||||
{
|
||||
"name": "borrow",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"amount": "100",
|
||||
"borrowRateMode": "variable",
|
||||
"user": "1",
|
||||
"timeTravel": "365"
|
||||
},
|
||||
"expected": "success"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "User 1 tries to borrow the rest of the DAI liquidity (revert expected)",
|
||||
"actions": [
|
||||
{
|
||||
"name": "borrow",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"amount": "900",
|
||||
"borrowRateMode": "variable",
|
||||
"user": "1"
|
||||
},
|
||||
"expected": "revert",
|
||||
"revertMessage": "There is not enough collateral to cover a new borrow"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "User 1 tries to repay with permit 0 DAI (revert expected)",
|
||||
"actions": [
|
||||
{
|
||||
"name": "repayWithPermit",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"amount": "0",
|
||||
"user": "1",
|
||||
"onBehalfOf": "1"
|
||||
},
|
||||
"expected": "revert",
|
||||
"revertMessage": "Amount must be greater than 0"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "User 1 repays with permit a small amount of DAI, enough to cover a small part of the interest",
|
||||
"actions": [
|
||||
{
|
||||
"name": "repayWithPermit",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"amount": "1.25",
|
||||
"user": "1",
|
||||
"onBehalfOf": "1",
|
||||
"borrowRateMode": "variable"
|
||||
},
|
||||
"expected": "success"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "User 1 repays with permit the DAI borrow after one year",
|
||||
"actions": [
|
||||
{
|
||||
"name": "mint",
|
||||
"description": "Mint 10 DAI to cover the interest",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"amount": "10",
|
||||
"user": "1"
|
||||
},
|
||||
"expected": "success"
|
||||
},
|
||||
{
|
||||
"name": "repayWithPermit",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"amount": "-1",
|
||||
"user": "1",
|
||||
"onBehalfOf": "1",
|
||||
"borrowRateMode": "variable"
|
||||
},
|
||||
"expected": "success"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "User 0 withdraws the deposited DAI plus interest",
|
||||
"actions": [
|
||||
{
|
||||
"name": "withdraw",
|
||||
"args": {
|
||||
"reserve": "DAI",
|
||||
"amount": "-1",
|
||||
"user": "0"
|
||||
},
|
||||
"expected": "success"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "User 1 withdraws the collateral",
|
||||
"actions": [
|
||||
{
|
||||
"name": "withdraw",
|
||||
"args": {
|
||||
"reserve": "WETH",
|
||||
"amount": "-1",
|
||||
"user": "1"
|
||||
},
|
||||
"expected": "success"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -35,7 +35,7 @@ fs.readdirSync(scenarioFolder).forEach((file) => {
|
|||
|
||||
for (const story of scenario.stories) {
|
||||
it(story.description, async function () {
|
||||
// Retry the test scenarios up to 4 times if an error happens, due erratic HEVM network errors
|
||||
// Retry the test scenarios up to 4 times if an error happens, due erratic HEVM network errors
|
||||
this.retries(4);
|
||||
await executeStory(story, testEnv);
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue
Block a user