Skip to content

Robust ovm prod tests #1130

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Mar 13, 2021
2 changes: 1 addition & 1 deletion publish/src/commands/deploy-ovm-pair.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ const deployInstance = async ({ useOvm, privateKey, l1ProviderUrl, l2ProviderUrl
methodCallGasLimit: '3500000',
contractDeploymentGasLimit: useOvm ? OVM_MAX_GAS_LIMIT : '9500000',
privateKey,
ignoreCustomParameters: true,
ignoreCustomParameters: false,
});
};

Expand Down
261 changes: 172 additions & 89 deletions test/optimism/synthExchange.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,153 @@ const itCanPerformSynthExchange = ({ ctx }) => {
ExchangerL2,
ExchangeRatesL2,
ExchangeStateL2,
FeePoolL2;
FeePoolL2,
SystemSettingsL2;

let user1sETHBalanceL2, user1sUSDBalanceL2;
// --------------------------
// Setup
// --------------------------

const itCanSettleL2 = async (canSettle, synth) => {
describe('When the user tries to settle', () => {
before('connect user to contract', async () => {
SynthetixL2 = SynthetixL2.connect(user1L2);
});
if (canSettle) {
it('settles correctly', async () => {
const tx = await SynthetixL2.settle(synth);
const receipt = await tx.wait();
if (!receipt) {
throw new Error(`Transaction reverted, even though it was not supposed to.`);
}
});
} else {
it('settling reverts', async () => {
const tx = await SynthetixL2.settle(synth);

await assertRevertOptimism({
tx,
reason: 'Cannot settle during waiting',
provider: ctx.providerL2,
});
});
}
});
};

const itHasExchangeEntriesL2 = async numEntries => {
describe('When checking ExhangeState', () => {
it(`${numEntries} exchange state entries should have been created`, async () => {
assert.bnEqual(
await ExchangeStateL2.getLengthOfEntries(user1L2.address, sETH),
numEntries
);
});
});
};

const itCanSetTheWaitingPeriodL2 = async waitingPeriod => {
describe(`When setting the waiting period to ${waitingPeriod}`, () => {
before('setWaitingPeriod', async () => {
SystemSettingsL2 = SystemSettingsL2.connect(ctx.ownerL2);
const tx = await SystemSettingsL2.setWaitingPeriodSecs(waitingPeriod);
await tx.wait();
});

it('waiting is set correctly', async () => {
assert.bnEqual(await ExchangerL2.waitingPeriodSecs(), waitingPeriod);
});
});
};

const itCanExchangeUsdToEthL2 = async sUSDtoBeExchanged => {
describe('when the user exchanges sUSD for sETH', () => {
let received;
let normalizedFee;
let feeAddresssUSDBalanceL2;
let feesToDistributeL2;
let user1sETHBalanceL2, user1sUSDBalanceL2;
const feeAddress = getUsers({ network: 'mainnet', user: 'fee' }).address;

before('record current values', async () => {
user1sETHBalanceL2 = await SynthsETHL2.balanceOf(user1L2.address);
user1sUSDBalanceL2 = await SynthsUSDL2.balanceOf(user1L2.address);
feeAddresssUSDBalanceL2 = await SynthsUSDL2.balanceOf(feeAddress);
const feePeriodZero = await FeePoolL2.recentFeePeriods(0);
feesToDistributeL2 = feePeriodZero.feesToDistribute;
});

before('connect user to contract', async () => {
SynthetixL2 = SynthetixL2.connect(user1L2);
});

before('sUSD to sETH exchange', async () => {
const tx = await SynthetixL2.exchange(sUSD, sUSDtoBeExchanged, sETH);
await tx.wait();
const { amountReceived, fee } = await ExchangerL2.getAmountsForExchange(
sUSDtoBeExchanged,
sUSD,
sETH
);
received = amountReceived;
normalizedFee = await ExchangeRatesL2.effectiveValue(sETH, fee, sUSD);
});

it('shows that the user L2 sUSD balance has decreased', async () => {
assert.bnEqual(
await SynthsUSDL2.balanceOf(user1L2.address),
user1sUSDBalanceL2.sub(sUSDtoBeExchanged)
);
});
it('shows that the user L2 sETH balance has increased', async () => {
assert.bnEqual(
await SynthsETHL2.balanceOf(user1L2.address),
user1sETHBalanceL2.add(received)
);
});
it('shows that the user fees have been recorded correctly', async () => {
const firstPeriod = await FeePoolL2.recentFeePeriods(0);

assert.bnEqual(firstPeriod.feePeriodId, '1');
assert.bnEqual(firstPeriod.feesToDistribute, feesToDistributeL2.add(normalizedFee));
assert.bnEqual(firstPeriod.feesClaimed, '0');
});
it('shows that the fees are initially remitted to the right address(0xfeEFEEfeefEeFeefEEFEEfEeFeefEEFeeFEEFEeF)', async () => {
// fee remittance
assert.bnEqual(
await SynthsUSDL2.balanceOf(feeAddress),
feeAddresssUSDBalanceL2.add(normalizedFee)
);
});
});
};

const itCanIssueL2 = async sUSDIssued => {
describe('When the user issues sUSD', () => {
let user1sETHBalanceL2, user1sUSDBalanceL2;
before('connect user to contract', async () => {
SynthetixL2 = SynthetixL2.connect(user1L2);
});
before('record current values', async () => {
user1sETHBalanceL2 = await SynthsETHL2.balanceOf(user1L2.address);
user1sUSDBalanceL2 = await SynthsUSDL2.balanceOf(user1L2.address);
});

before(`issue ${sUSDIssued} sUSD`, async () => {
const tx = await SynthetixL2.issueSynths(sUSDIssued);
await tx.wait();
});

it('shows that the user L2 sUSD balance has increased (while all other synth balacnes remain the same)', async () => {
assert.bnEqual(
await SynthsUSDL2.balanceOf(user1L2.address),
user1sUSDBalanceL2.add(sUSDIssued)
);
assert.bnEqual(await SynthsETHL2.balanceOf(user1L2.address), user1sETHBalanceL2);
});
});
};

before('identify signers', async () => {
user1L1 = ctx.providerL1.getSigner(ctx.user1Address);
user1L1.address = ctx.user1Address;
Expand Down Expand Up @@ -86,26 +226,17 @@ const itCanPerformSynthExchange = ({ ctx }) => {
useOvm: true,
provider: ctx.providerL2,
});
SystemSettingsL2 = connectContract({
contract: 'SystemSettings',
useOvm: true,
provider: ctx.providerL2,
});
});

// --------------------------
// Get SNX
// --------------------------

describe('Initial values', () => {
before('record current values', async () => {
user1sETHBalanceL2 = await SynthsETHL2.balanceOf(user1L2.address);
user1sUSDBalanceL2 = await SynthsETHL2.balanceOf(user1L2.address);
});

it('the waiting period is 360 on L2', async () => {
assert.bnEqual(await ExchangerL2.waitingPeriodSecs(), '300');
});
it('the initial sETH balance is 0', async () => {
assert.bnEqual(await SynthsETHL2.balanceOf(user1L2.address), '0');
});
});

describe('when a user has the expected amount of SNX in L1', () => {
let user1BalanceL1;

Expand Down Expand Up @@ -214,80 +345,32 @@ const itCanPerformSynthExchange = ({ ctx }) => {
);
});

describe('when the user issues sUSD', () => {
describe('When the waiting period is 0', () => {
const sUSDIssued = ethers.utils.parseEther('10');
itCanSetTheWaitingPeriodL2('0');
itCanIssueL2(sUSDIssued);
itCanExchangeUsdToEthL2(sUSDIssued);
// since the waiting period is 0 is should skip cerating exchange entries (SIP-118)
itHasExchangeEntriesL2('0');
// since the waiting period is 0 it settle should not fail, it just has no effect
itCanSettleL2(true, sETH);
});

describe('When the waiting period is greater than 0', () => {
const sUSDIssued = ethers.utils.parseEther('10');
before('issue sUSD', async () => {
SynthetixL2 = SynthetixL2.connect(user1L2);

const tx = await SynthetixL2.issueSynths(sUSDIssued);
await tx.wait();
});

it('shows that the user L2 sUSD balance has increased (while all other synth balacnes remain the same)', async () => {
assert.bnEqual(
await SynthsUSDL2.balanceOf(user1L2.address),
user1sUSDBalanceL2.add(sUSDIssued)
);
assert.bnEqual(await SynthsETHL2.balanceOf(user1L2.address), user1sETHBalanceL2);
});

describe('when the exchanges sUSD for sETH', () => {
let received;
let normalizedFee;
before('sETH exchange', async () => {
const tx = await SynthetixL2.exchange(sUSD, sUSDIssued, sETH);
await tx.wait();
const { amountReceived, fee } = await ExchangerL2.getAmountsForExchange(
sUSDIssued,
sUSD,
sETH
);
received = amountReceived;
normalizedFee = await ExchangeRatesL2.effectiveValue(sETH, fee, sUSD);
});

it('shows that the user L2 sUSD balance has decreased', async () => {
assert.bnEqual(
await SynthsUSDL2.balanceOf(user1L2.address),
user1sUSDBalanceL2
);
});
it('shows that the user L2 sETH balance has increased', async () => {
assert.bnEqual(
await SynthsETHL2.balanceOf(user1L2.address),
user1sETHBalanceL2.add(received)
);
});
it('shows that the user fees have been recorded correctly', async () => {
const firstPeriod = await FeePoolL2.recentFeePeriods(0);

assert.bnEqual(firstPeriod.feePeriodId, '1');
assert.bnEqual(firstPeriod.feesToDistribute, normalizedFee);
assert.bnEqual(firstPeriod.feesClaimed, '0');
});
it('shows that the fees are initially remitted to the right address(0xfeEFEEfeefEeFeefEEFEEfEeFeefEEFeeFEEFEeF)', async () => {
// fee remittance
const feeAddress = getUsers({ network: 'mainnet', user: 'fee' }).address;
assert.bnEqual(await SynthsUSDL2.balanceOf(feeAddress), normalizedFee);
});
it('should create an exchange state entry', async () => {
assert.bnEqual(
await ExchangeStateL2.getLengthOfEntries(user1L2.address, sETH),
'1'
);
});
describe('when settling the exchange', () => {
it('reverts when trying to settle without waiting', async () => {
const tx = await SynthetixL2.settle(toBytes32('sETH'));

await assertRevertOptimism({
tx,
reason: 'Cannot settle during waiting',
provider: ctx.providerL2,
});
});
});
});
itCanSetTheWaitingPeriodL2('360');
itCanIssueL2(sUSDIssued);
itCanExchangeUsdToEthL2(sUSDIssued);
// since the waiting period is gt 0 it should have created exchange entries
itHasExchangeEntriesL2('1');
// since the waiting period is gt 0 it should not be possible to settle immediately, hence the fist argument is false
itCanSettleL2(false, sETH);
// since settlement fails, the entries should persist
itHasExchangeEntriesL2('1');
// set the waiting period to 0
itCanSetTheWaitingPeriodL2('0');
// it should be able to settle now!
itCanSettleL2(true, sETH);
});
});
});
Expand Down
24 changes: 16 additions & 8 deletions test/optimism/utils/revertOptimism.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
const ethers = require('ethers');

function _hexToString(hex) {
let str = '';

const terminator = '**zÛ';
for (var i = 0; i < hex.length; i += 2) {
str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));

if (str.includes(terminator)) {
break;
}
}

return str.substring(0, str.length - 4);
}

async function getOptimismRevertReason({ tx, provider }) {
try {
let code = await provider.call(tx);
Expand All @@ -10,14 +25,7 @@ async function getOptimismRevertReason({ tx, provider }) {
if (code.length === 64) {
reason = ethers.utils.parseBytes32String(`0x${code}`);
} else {
reason = '';
const chunks = code.match(/.{1,62}/g);
chunks.map(chunk => {
try {
const parsed = ethers.utils.toUtf8String(`0x${chunk}00`);
reason += parsed;
} catch (error) {}
});
reason = _hexToString(`0x${code}`);
}

return reason;
Expand Down
Loading