Skip to content

Commit

Permalink
sdk: fix tests and add changelog entry for interactive UDC withdraw
Browse files Browse the repository at this point in the history
  • Loading branch information
andrevmatos committed Mar 24, 2021
1 parent 4bb9444 commit f9c13f9
Show file tree
Hide file tree
Showing 3 changed files with 261 additions and 215 deletions.
1 change: 1 addition & 0 deletions raiden-ts/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
### Added
- [#1342] Flat (fixed) mediation fees for mediator nodes
- [#2581] `config.pfsSafetyMargin` now also accepts a `[f, a]` pair, which will add `f*fee + a*amount` on top of PFS's estimated fee, if one wants finer-grain control on safety margin which is added on the transfer to be initiated.
- [#2629] `config.autoUdcWithdraw` (default=true) to allow disabling automatically completing a planned UDC withdraw, and new `Raiden.getUdcWithdrawPlan` and `Raiden.udcWithdraw` to check and perform UDC withdraw when not in auto mode.

### Changed
- [#2536] Wait for global messages before resolving deposits and channel open request
Expand Down
191 changes: 2 additions & 189 deletions raiden-ts/tests/unit/epics/monitor.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,16 @@ import {
makeAddress,
makeHash,
makeLog,
makeRaiden,
makeRaidens,
makeTransaction,
mockedSignMessage,
originalSignMessage,
providersEmit,
sleep,
waitBlock,
} from '../mocks';

import { defaultAbiCoder } from '@ethersproject/abi';
import { BigNumber } from '@ethersproject/bignumber';
import { MaxUint256, Two, WeiPerEther, Zero } from '@ethersproject/constants';
import { Two, WeiPerEther } from '@ethersproject/constants';
import { first, pluck } from 'rxjs/operators';

import { raidenConfigUpdate, raidenShutdown } from '@/actions';
Expand All @@ -36,37 +33,7 @@ import { MessageType } from '@/messages/types';
import { createBalanceHash } from '@/messages/utils';
import { msBalanceProofSent, udcDeposit } from '@/services/actions';
import { Service } from '@/services/types';
import { ErrorCodes } from '@/utils/error';
import type { Hash, UInt } from '@/utils/types';

test('monitorUdcBalanceEpic', async () => {
expect.assertions(5);

const raiden = await makeRaiden(undefined, false);
const { userDepositContract } = raiden.deps;
userDepositContract.effectiveBalance.mockResolvedValue(Zero);
userDepositContract.total_deposit.mockResolvedValue(Zero);

await raiden.start();
await sleep();
expect(raiden.output).toContainEqual(
udcDeposit.success({ balance: Zero as UInt<32> }, { totalDeposit: Zero as UInt<32> }),
);
await expect(
raiden.deps.latest$.pipe(pluck('udcBalance'), first()).toPromise(),
).resolves.toEqual(Zero);

const balance = BigNumber.from(23) as UInt<32>;
userDepositContract.effectiveBalance.mockResolvedValue(balance);
userDepositContract.total_deposit.mockResolvedValue(balance);
await waitBlock();

expect(raiden.output).toContainEqual(udcDeposit.success({ balance }, { totalDeposit: balance }));
await expect(
raiden.deps.latest$.pipe(pluck('udcBalance'), first()).toPromise(),
).resolves.toEqual(balance);
expect(userDepositContract.effectiveBalance).toHaveBeenCalledTimes(2);
});
import type { UInt } from '@/utils/types';

describe('msMonitorRequestEpic', () => {
test('success: receiving a transfer triggers monitoring', async () => {
Expand Down Expand Up @@ -383,157 +350,3 @@ test('msMonitorNewBPEpic', async () => {
}),
);
});

describe('udcDepositEpic', () => {
const deposit = BigNumber.from(10) as UInt<32>;

test('fails if not enough balance', async () => {
expect.assertions(1);

const raiden = await makeRaiden(undefined, false);
const { userDepositContract } = raiden.deps;
userDepositContract.effectiveBalance.mockResolvedValue(Zero);
userDepositContract.total_deposit.mockResolvedValue(Zero);

const tokenContract = raiden.deps.getTokenContract(
await raiden.deps.userDepositContract.token(),
);
tokenContract.balanceOf.mockResolvedValue(deposit.sub(1));

await raiden.start();
raiden.store.dispatch(udcDeposit.request({ deposit }, { totalDeposit: deposit }));
await waitBlock();

expect(raiden.output).toContainEqual(
udcDeposit.failure(
expect.objectContaining({ message: ErrorCodes.RDN_INSUFFICIENT_BALANCE }),
{ totalDeposit: deposit },
),
);
});

test('approve tx fails with resetAllowance needed', async () => {
expect.assertions(4);

const raiden = await makeRaiden(undefined, false);
const { userDepositContract } = raiden.deps;
userDepositContract.effectiveBalance.mockResolvedValue(Zero);
userDepositContract.total_deposit.mockResolvedValue(Zero);

const tokenContract = raiden.deps.getTokenContract(
await raiden.deps.userDepositContract.token(),
);
// not enough allowance, but not zero, need to reset
tokenContract.allowance.mockResolvedValue(deposit.sub(1));

tokenContract.approve.mockResolvedValue(makeTransaction(0, { to: tokenContract.address }));
// resetAllowance$ succeeds, but then actual approve fails
tokenContract.approve.mockResolvedValueOnce(
makeTransaction(undefined, { to: tokenContract.address }),
);

await raiden.start();
raiden.store.dispatch(udcDeposit.request({ deposit }, { totalDeposit: deposit }));
await waitBlock();

expect(raiden.output).toContainEqual(
udcDeposit.failure(
expect.objectContaining({ message: ErrorCodes.RDN_APPROVE_TRANSACTION_FAILED }),
{ totalDeposit: deposit },
),
);
expect(tokenContract.approve).toHaveBeenCalledTimes(2);
expect(tokenContract.approve).toHaveBeenCalledWith(userDepositContract.address, 0);
expect(tokenContract.approve).toHaveBeenCalledWith(
userDepositContract.address,
raiden.config.minimumAllowance,
);
});

test('deposit tx fails', async () => {
expect.assertions(1);

const raiden = await makeRaiden(undefined, false);
const { userDepositContract } = raiden.deps;
userDepositContract.effectiveBalance.mockResolvedValue(Zero);
userDepositContract.total_deposit.mockResolvedValue(Zero);

const tokenContract = raiden.deps.getTokenContract(
await raiden.deps.userDepositContract.token(),
);

const approveTx = makeTransaction(undefined, { to: tokenContract.address });
tokenContract.approve.mockResolvedValue(approveTx);

const depositTx = makeTransaction(0, { to: userDepositContract.address });
userDepositContract.deposit.mockResolvedValue(depositTx);

await raiden.start();
raiden.store.dispatch(udcDeposit.request({ deposit }, { totalDeposit: deposit }));
await waitBlock();

expect(raiden.output).toContainEqual(
udcDeposit.failure(
expect.objectContaining({ message: ErrorCodes.RDN_DEPOSIT_TRANSACTION_FAILED }),
{ totalDeposit: deposit },
),
);
});

test('success', async () => {
expect.assertions(8);

const prevDeposit = BigNumber.from(330) as UInt<32>;
const balance = prevDeposit.add(deposit) as UInt<32>;
const raiden = await makeRaiden(undefined, false);
const { userDepositContract } = raiden.deps;
userDepositContract.effectiveBalance.mockResolvedValue(prevDeposit);
userDepositContract.total_deposit.mockResolvedValue(prevDeposit);

const tokenContract = raiden.deps.getTokenContract(
await raiden.deps.userDepositContract.token(),
);
// allowance first isn't enough but not zero, resetAllowance needed
tokenContract.allowance.mockResolvedValue(deposit.sub(1));

const approveTx = makeTransaction(undefined, { to: tokenContract.address });
tokenContract.approve.mockResolvedValue(approveTx);
// first approve tx fail with nonce error, replacement fee error should be retried
const approveFailTx: typeof approveTx = makeTransaction(undefined, {
to: tokenContract.address,
wait: jest.fn().mockRejectedValue(new Error('replacement fee too low')),
});
tokenContract.approve.mockResolvedValueOnce(approveFailTx);

const depositTx = makeTransaction(undefined, { to: userDepositContract.address });
userDepositContract.deposit.mockResolvedValue(depositTx);
userDepositContract.effectiveBalance.mockResolvedValue(balance);

await raiden.start();
raiden.store.dispatch(udcDeposit.request({ deposit }, { totalDeposit: balance }));
await waitBlock();
// give some time for the `approve` retry
await sleep(raiden.deps.provider.pollingInterval * 2);

// result is undefined on success as the respective udcDeposit.success is emitted by the
// channelMonitoredEpic, which monitors the blockchain for ChannelNewDeposit events
expect(raiden.output).not.toContainEqual(
udcDeposit.failure(expect.any(Error), expect.anything()),
);
expect(raiden.output).toContainEqual(
udcDeposit.success(
{ balance, txHash: depositTx.hash! as Hash, txBlock: expect.any(Number), confirmed: true },
{ totalDeposit: balance },
),
);
expect(tokenContract.approve).toHaveBeenCalledTimes(3);
expect(approveTx.wait).toHaveBeenCalledTimes(2);
expect(tokenContract.approve).toHaveBeenCalledWith(userDepositContract.address, MaxUint256);
expect(userDepositContract.deposit).toHaveBeenCalledTimes(1);
expect(userDepositContract.deposit).toHaveBeenCalledWith(
raiden.address,
deposit.add(prevDeposit),
);
expect(depositTx.wait).toHaveBeenCalledTimes(1);
});
});
Loading

0 comments on commit f9c13f9

Please sign in to comment.