Fix indentations
This commit is contained in:
@@ -18,100 +18,101 @@ const expect = chai.expect;
|
||||
const web3: Web3 = (global as any).web3;
|
||||
|
||||
contract('EtherToken', (accounts: string[]) => {
|
||||
const account = accounts[0];
|
||||
const gasPrice = ZeroEx.toBaseUnitAmount(new BigNumber(20), 9);
|
||||
let zeroEx: ZeroEx;
|
||||
let etherTokenAddress: string;
|
||||
before(async () => {
|
||||
etherTokenAddress = EtherToken.address;
|
||||
zeroEx = new ZeroEx(web3.currentProvider, {
|
||||
gasPrice,
|
||||
etherTokenContractAddress: etherTokenAddress,
|
||||
});
|
||||
});
|
||||
|
||||
const sendTransactionAsync = promisify(web3.eth.sendTransaction);
|
||||
const getEthBalanceAsync = async (owner: string) => {
|
||||
const balanceStr = await promisify(web3.eth.getBalance)(owner);
|
||||
const balance = new BigNumber(balanceStr);
|
||||
return balance;
|
||||
};
|
||||
|
||||
describe('deposit', () => {
|
||||
it('should throw if caller attempts to deposit more Ether than caller balance', async () => {
|
||||
const initEthBalance = await getEthBalanceAsync(account);
|
||||
const ethToDeposit = initEthBalance.plus(1);
|
||||
|
||||
return expect(zeroEx.etherToken.depositAsync(ethToDeposit, account))
|
||||
.to.be.rejectedWith(ZeroExError.InsufficientEthBalanceForDeposit);
|
||||
const account = accounts[0];
|
||||
const gasPrice = ZeroEx.toBaseUnitAmount(new BigNumber(20), 9);
|
||||
let zeroEx: ZeroEx;
|
||||
let etherTokenAddress: string;
|
||||
before(async () => {
|
||||
etherTokenAddress = EtherToken.address;
|
||||
zeroEx = new ZeroEx(web3.currentProvider, {
|
||||
gasPrice,
|
||||
etherTokenContractAddress: etherTokenAddress,
|
||||
});
|
||||
});
|
||||
|
||||
it('should convert deposited Ether to wrapped Ether tokens', async () => {
|
||||
const initEthBalance = await getEthBalanceAsync(account);
|
||||
const initEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account);
|
||||
const sendTransactionAsync = promisify(web3.eth.sendTransaction);
|
||||
const getEthBalanceAsync = async (owner: string) => {
|
||||
const balanceStr = await promisify(web3.eth.getBalance)(owner);
|
||||
const balance = new BigNumber(balanceStr);
|
||||
return balance;
|
||||
};
|
||||
|
||||
const ethToDeposit = new BigNumber(web3.toWei(1, 'ether'));
|
||||
describe('deposit', () => {
|
||||
it('should throw if caller attempts to deposit more Ether than caller balance', async () => {
|
||||
const initEthBalance = await getEthBalanceAsync(account);
|
||||
const ethToDeposit = initEthBalance.plus(1);
|
||||
|
||||
const txHash = await zeroEx.etherToken.depositAsync(ethToDeposit, account);
|
||||
const receipt = await zeroEx.awaitTransactionMinedAsync(txHash);
|
||||
return expect(zeroEx.etherToken.depositAsync(ethToDeposit, account))
|
||||
.to.be.rejectedWith(ZeroExError.InsufficientEthBalanceForDeposit);
|
||||
});
|
||||
|
||||
const ethSpentOnGas = gasPrice.times(receipt.gasUsed);
|
||||
const finalEthBalance = await getEthBalanceAsync(account);
|
||||
const finalEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account);
|
||||
it('should convert deposited Ether to wrapped Ether tokens', async () => {
|
||||
const initEthBalance = await getEthBalanceAsync(account);
|
||||
const initEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account);
|
||||
|
||||
expect(finalEthBalance).to.be.bignumber.equal(initEthBalance.minus(ethToDeposit.plus(ethSpentOnGas)));
|
||||
expect(finalEthTokenBalance).to.be.bignumber.equal(initEthTokenBalance.plus(ethToDeposit));
|
||||
});
|
||||
});
|
||||
const ethToDeposit = new BigNumber(web3.toWei(1, 'ether'));
|
||||
|
||||
describe('withdraw', () => {
|
||||
it('should throw if caller attempts to withdraw greater than caller balance', async () => {
|
||||
const initEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account);
|
||||
const ethTokensToWithdraw = initEthTokenBalance.plus(1);
|
||||
const txHash = await zeroEx.etherToken.depositAsync(ethToDeposit, account);
|
||||
const receipt = await zeroEx.awaitTransactionMinedAsync(txHash);
|
||||
|
||||
return expect(zeroEx.etherToken.withdrawAsync(ethTokensToWithdraw, account))
|
||||
.to.be.rejectedWith(ZeroExError.InsufficientWEthBalanceForWithdrawal);
|
||||
const ethSpentOnGas = gasPrice.times(receipt.gasUsed);
|
||||
const finalEthBalance = await getEthBalanceAsync(account);
|
||||
const finalEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account);
|
||||
|
||||
expect(finalEthBalance).to.be.bignumber.equal(initEthBalance.minus(ethToDeposit.plus(ethSpentOnGas)));
|
||||
expect(finalEthTokenBalance).to.be.bignumber.equal(initEthTokenBalance.plus(ethToDeposit));
|
||||
});
|
||||
});
|
||||
|
||||
it('should convert ether tokens to ether with sufficient balance', async () => {
|
||||
const initEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account);
|
||||
const initEthBalance = await getEthBalanceAsync(account);
|
||||
const ethTokensToWithdraw = initEthTokenBalance;
|
||||
expect(ethTokensToWithdraw).to.not.be.bignumber.equal(0);
|
||||
const txHash = await zeroEx.etherToken.withdrawAsync(ethTokensToWithdraw, account);
|
||||
const receipt = await zeroEx.awaitTransactionMinedAsync(txHash);
|
||||
describe('withdraw', () => {
|
||||
it('should throw if caller attempts to withdraw greater than caller balance', async () => {
|
||||
const initEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account);
|
||||
const ethTokensToWithdraw = initEthTokenBalance.plus(1);
|
||||
|
||||
const ethSpentOnGas = gasPrice.times(receipt.gasUsed);
|
||||
const finalEthBalance = await getEthBalanceAsync(account);
|
||||
const finalEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account);
|
||||
return expect(zeroEx.etherToken.withdrawAsync(ethTokensToWithdraw, account))
|
||||
.to.be.rejectedWith(ZeroExError.InsufficientWEthBalanceForWithdrawal);
|
||||
});
|
||||
|
||||
expect(finalEthBalance).to.be.bignumber.equal(initEthBalance.plus(ethTokensToWithdraw.minus(ethSpentOnGas)));
|
||||
expect(finalEthTokenBalance).to.be.bignumber.equal(initEthTokenBalance.minus(ethTokensToWithdraw));
|
||||
it('should convert ether tokens to ether with sufficient balance', async () => {
|
||||
const initEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account);
|
||||
const initEthBalance = await getEthBalanceAsync(account);
|
||||
const ethTokensToWithdraw = initEthTokenBalance;
|
||||
expect(ethTokensToWithdraw).to.not.be.bignumber.equal(0);
|
||||
const txHash = await zeroEx.etherToken.withdrawAsync(ethTokensToWithdraw, account);
|
||||
const receipt = await zeroEx.awaitTransactionMinedAsync(txHash);
|
||||
|
||||
const ethSpentOnGas = gasPrice.times(receipt.gasUsed);
|
||||
const finalEthBalance = await getEthBalanceAsync(account);
|
||||
const finalEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account);
|
||||
|
||||
expect(finalEthBalance).to.be.bignumber
|
||||
.equal(initEthBalance.plus(ethTokensToWithdraw.minus(ethSpentOnGas)));
|
||||
expect(finalEthTokenBalance).to.be.bignumber.equal(initEthTokenBalance.minus(ethTokensToWithdraw));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('fallback', () => {
|
||||
it('should convert sent ether to ether tokens', async () => {
|
||||
const initEthBalance = await getEthBalanceAsync(account);
|
||||
const initEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account);
|
||||
describe('fallback', () => {
|
||||
it('should convert sent ether to ether tokens', async () => {
|
||||
const initEthBalance = await getEthBalanceAsync(account);
|
||||
const initEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account);
|
||||
|
||||
const ethToDeposit = ZeroEx.toBaseUnitAmount(new BigNumber(1), 18);
|
||||
const ethToDeposit = ZeroEx.toBaseUnitAmount(new BigNumber(1), 18);
|
||||
|
||||
const txHash = await sendTransactionAsync({
|
||||
from: account,
|
||||
to: etherTokenAddress,
|
||||
value: ethToDeposit,
|
||||
gasPrice,
|
||||
});
|
||||
const txHash = await sendTransactionAsync({
|
||||
from: account,
|
||||
to: etherTokenAddress,
|
||||
value: ethToDeposit,
|
||||
gasPrice,
|
||||
});
|
||||
|
||||
const receipt = await zeroEx.awaitTransactionMinedAsync(txHash);
|
||||
const receipt = await zeroEx.awaitTransactionMinedAsync(txHash);
|
||||
|
||||
const ethSpentOnGas = gasPrice.times(receipt.gasUsed);
|
||||
const finalEthBalance = await getEthBalanceAsync(account);
|
||||
const finalEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account);
|
||||
const ethSpentOnGas = gasPrice.times(receipt.gasUsed);
|
||||
const finalEthBalance = await getEthBalanceAsync(account);
|
||||
const finalEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account);
|
||||
|
||||
expect(finalEthBalance).to.be.bignumber.equal(initEthBalance.minus(ethToDeposit.plus(ethSpentOnGas)));
|
||||
expect(finalEthTokenBalance).to.be.bignumber.equal(initEthTokenBalance.plus(ethToDeposit));
|
||||
expect(finalEthBalance).to.be.bignumber.equal(initEthBalance.minus(ethToDeposit.plus(ethSpentOnGas)));
|
||||
expect(finalEthTokenBalance).to.be.bignumber.equal(initEthTokenBalance.plus(ethToDeposit));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -13,161 +13,161 @@ chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
|
||||
const {
|
||||
Exchange,
|
||||
TokenRegistry,
|
||||
Exchange,
|
||||
TokenRegistry,
|
||||
} = new Artifacts(artifacts);
|
||||
|
||||
contract('Exchange', (accounts: string[]) => {
|
||||
const maker = accounts[0];
|
||||
const feeRecipient = accounts[1] || accounts[accounts.length - 1];
|
||||
const maker = accounts[0];
|
||||
const feeRecipient = accounts[1] || accounts[accounts.length - 1];
|
||||
|
||||
let order: Order;
|
||||
let exchangeWrapper: ExchangeWrapper;
|
||||
let orderFactory: OrderFactory;
|
||||
let order: Order;
|
||||
let exchangeWrapper: ExchangeWrapper;
|
||||
let orderFactory: OrderFactory;
|
||||
|
||||
before(async () => {
|
||||
const [tokenRegistry, exchange] = await Promise.all([
|
||||
TokenRegistry.deployed(),
|
||||
Exchange.deployed(),
|
||||
]);
|
||||
exchangeWrapper = new ExchangeWrapper(exchange);
|
||||
const [repAddress, dgdAddress] = await Promise.all([
|
||||
tokenRegistry.getTokenAddressBySymbol('REP'),
|
||||
tokenRegistry.getTokenAddressBySymbol('DGD'),
|
||||
]);
|
||||
const defaultOrderParams = {
|
||||
exchangeContractAddress: Exchange.address,
|
||||
maker,
|
||||
feeRecipient,
|
||||
makerToken: repAddress,
|
||||
takerToken: dgdAddress,
|
||||
makerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
|
||||
takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18),
|
||||
makerFee: ZeroEx.toBaseUnitAmount(new BigNumber(1), 18),
|
||||
takerFee: ZeroEx.toBaseUnitAmount(new BigNumber(1), 18),
|
||||
};
|
||||
orderFactory = new OrderFactory(defaultOrderParams);
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
order = await orderFactory.newSignedOrderAsync();
|
||||
});
|
||||
|
||||
describe('getOrderHash', () => {
|
||||
it('should output the correct orderHash', async () => {
|
||||
const orderHashHex = await exchangeWrapper.getOrderHashAsync(order);
|
||||
expect(order.params.orderHashHex).to.be.equal(orderHashHex);
|
||||
before(async () => {
|
||||
const [tokenRegistry, exchange] = await Promise.all([
|
||||
TokenRegistry.deployed(),
|
||||
Exchange.deployed(),
|
||||
]);
|
||||
exchangeWrapper = new ExchangeWrapper(exchange);
|
||||
const [repAddress, dgdAddress] = await Promise.all([
|
||||
tokenRegistry.getTokenAddressBySymbol('REP'),
|
||||
tokenRegistry.getTokenAddressBySymbol('DGD'),
|
||||
]);
|
||||
const defaultOrderParams = {
|
||||
exchangeContractAddress: Exchange.address,
|
||||
maker,
|
||||
feeRecipient,
|
||||
makerToken: repAddress,
|
||||
takerToken: dgdAddress,
|
||||
makerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
|
||||
takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18),
|
||||
makerFee: ZeroEx.toBaseUnitAmount(new BigNumber(1), 18),
|
||||
takerFee: ZeroEx.toBaseUnitAmount(new BigNumber(1), 18),
|
||||
};
|
||||
orderFactory = new OrderFactory(defaultOrderParams);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isValidSignature', () => {
|
||||
beforeEach(async () => {
|
||||
order = await orderFactory.newSignedOrderAsync();
|
||||
order = await orderFactory.newSignedOrderAsync();
|
||||
});
|
||||
|
||||
it('should return true with a valid signature', async () => {
|
||||
const success = await exchangeWrapper.isValidSignatureAsync(order);
|
||||
const isValidSignature = order.isValidSignature();
|
||||
expect(isValidSignature).to.be.true();
|
||||
expect(success).to.be.true();
|
||||
describe('getOrderHash', () => {
|
||||
it('should output the correct orderHash', async () => {
|
||||
const orderHashHex = await exchangeWrapper.getOrderHashAsync(order);
|
||||
expect(order.params.orderHashHex).to.be.equal(orderHashHex);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return false with an invalid signature', async () => {
|
||||
order.params.r = ethUtil.bufferToHex(ethUtil.sha3('invalidR'));
|
||||
order.params.s = ethUtil.bufferToHex(ethUtil.sha3('invalidS'));
|
||||
const success = await exchangeWrapper.isValidSignatureAsync(order);
|
||||
expect(order.isValidSignature()).to.be.false();
|
||||
expect(success).to.be.false();
|
||||
});
|
||||
});
|
||||
describe('isValidSignature', () => {
|
||||
beforeEach(async () => {
|
||||
order = await orderFactory.newSignedOrderAsync();
|
||||
});
|
||||
|
||||
describe('isRoundingError', () => {
|
||||
it('should return false if there is a rounding error of 0.1%', async () => {
|
||||
const numerator = new BigNumber(20);
|
||||
const denominator = new BigNumber(999);
|
||||
const target = new BigNumber(50);
|
||||
// rounding error = ((20*50/999) - floor(20*50/999)) / (20*50/999) = 0.1%
|
||||
const isRoundingError = await exchangeWrapper.isRoundingErrorAsync(numerator, denominator, target);
|
||||
expect(isRoundingError).to.be.false();
|
||||
it('should return true with a valid signature', async () => {
|
||||
const success = await exchangeWrapper.isValidSignatureAsync(order);
|
||||
const isValidSignature = order.isValidSignature();
|
||||
expect(isValidSignature).to.be.true();
|
||||
expect(success).to.be.true();
|
||||
});
|
||||
|
||||
it('should return false with an invalid signature', async () => {
|
||||
order.params.r = ethUtil.bufferToHex(ethUtil.sha3('invalidR'));
|
||||
order.params.s = ethUtil.bufferToHex(ethUtil.sha3('invalidS'));
|
||||
const success = await exchangeWrapper.isValidSignatureAsync(order);
|
||||
expect(order.isValidSignature()).to.be.false();
|
||||
expect(success).to.be.false();
|
||||
});
|
||||
});
|
||||
|
||||
it('should return false if there is a rounding of 0.09%', async () => {
|
||||
const numerator = new BigNumber(20);
|
||||
const denominator = new BigNumber(9991);
|
||||
const target = new BigNumber(500);
|
||||
// rounding error = ((20*500/9991) - floor(20*500/9991)) / (20*500/9991) = 0.09%
|
||||
const isRoundingError = await exchangeWrapper.isRoundingErrorAsync(numerator, denominator, target);
|
||||
expect(isRoundingError).to.be.false();
|
||||
describe('isRoundingError', () => {
|
||||
it('should return false if there is a rounding error of 0.1%', async () => {
|
||||
const numerator = new BigNumber(20);
|
||||
const denominator = new BigNumber(999);
|
||||
const target = new BigNumber(50);
|
||||
// rounding error = ((20*50/999) - floor(20*50/999)) / (20*50/999) = 0.1%
|
||||
const isRoundingError = await exchangeWrapper.isRoundingErrorAsync(numerator, denominator, target);
|
||||
expect(isRoundingError).to.be.false();
|
||||
});
|
||||
|
||||
it('should return false if there is a rounding of 0.09%', async () => {
|
||||
const numerator = new BigNumber(20);
|
||||
const denominator = new BigNumber(9991);
|
||||
const target = new BigNumber(500);
|
||||
// rounding error = ((20*500/9991) - floor(20*500/9991)) / (20*500/9991) = 0.09%
|
||||
const isRoundingError = await exchangeWrapper.isRoundingErrorAsync(numerator, denominator, target);
|
||||
expect(isRoundingError).to.be.false();
|
||||
});
|
||||
|
||||
it('should return true if there is a rounding error of 0.11%', async () => {
|
||||
const numerator = new BigNumber(20);
|
||||
const denominator = new BigNumber(9989);
|
||||
const target = new BigNumber(500);
|
||||
// rounding error = ((20*500/9989) - floor(20*500/9989)) / (20*500/9989) = 0.011%
|
||||
const isRoundingError = await exchangeWrapper.isRoundingErrorAsync(numerator, denominator, target);
|
||||
expect(isRoundingError).to.be.true();
|
||||
});
|
||||
|
||||
it('should return true if there is a rounding error > 0.1%', async () => {
|
||||
const numerator = new BigNumber(3);
|
||||
const denominator = new BigNumber(7);
|
||||
const target = new BigNumber(10);
|
||||
// rounding error = ((3*10/7) - floor(3*10/7)) / (3*10/7) = 6.67%
|
||||
const isRoundingError = await exchangeWrapper.isRoundingErrorAsync(numerator, denominator, target);
|
||||
expect(isRoundingError).to.be.true();
|
||||
});
|
||||
|
||||
it('should return false when there is no rounding error', async () => {
|
||||
const numerator = new BigNumber(1);
|
||||
const denominator = new BigNumber(2);
|
||||
const target = new BigNumber(10);
|
||||
|
||||
const isRoundingError = await exchangeWrapper.isRoundingErrorAsync(numerator, denominator, target);
|
||||
expect(isRoundingError).to.be.false();
|
||||
});
|
||||
|
||||
it('should return false when there is rounding error <= 0.1%', async () => {
|
||||
// randomly generated numbers
|
||||
const numerator = new BigNumber(76564);
|
||||
const denominator = new BigNumber(676373677);
|
||||
const target = new BigNumber(105762562);
|
||||
// rounding error = ((76564*105762562/676373677) - floor(76564*105762562/676373677)) /
|
||||
// (76564*105762562/676373677) = 0.0007%
|
||||
const isRoundingError = await exchangeWrapper.isRoundingErrorAsync(numerator, denominator, target);
|
||||
expect(isRoundingError).to.be.false();
|
||||
});
|
||||
});
|
||||
|
||||
it('should return true if there is a rounding error of 0.11%', async () => {
|
||||
const numerator = new BigNumber(20);
|
||||
const denominator = new BigNumber(9989);
|
||||
const target = new BigNumber(500);
|
||||
// rounding error = ((20*500/9989) - floor(20*500/9989)) / (20*500/9989) = 0.011%
|
||||
const isRoundingError = await exchangeWrapper.isRoundingErrorAsync(numerator, denominator, target);
|
||||
expect(isRoundingError).to.be.true();
|
||||
describe('getPartialAmount', () => {
|
||||
it('should return the numerator/denominator*target', async () => {
|
||||
const numerator = new BigNumber(1);
|
||||
const denominator = new BigNumber(2);
|
||||
const target = new BigNumber(10);
|
||||
|
||||
const partialAmount = await exchangeWrapper.getPartialAmountAsync(numerator, denominator, target);
|
||||
const expectedPartialAmount = 5;
|
||||
expect(partialAmount).to.be.bignumber.equal(expectedPartialAmount);
|
||||
});
|
||||
|
||||
it('should round down', async () => {
|
||||
const numerator = new BigNumber(2);
|
||||
const denominator = new BigNumber(3);
|
||||
const target = new BigNumber(10);
|
||||
|
||||
const partialAmount = await exchangeWrapper.getPartialAmountAsync(numerator, denominator, target);
|
||||
const expectedPartialAmount = 6;
|
||||
expect(partialAmount).to.be.bignumber.equal(expectedPartialAmount);
|
||||
});
|
||||
|
||||
it('should round .5 down', async () => {
|
||||
const numerator = new BigNumber(1);
|
||||
const denominator = new BigNumber(20);
|
||||
const target = new BigNumber(10);
|
||||
|
||||
const partialAmount = await exchangeWrapper.getPartialAmountAsync(numerator, denominator, target);
|
||||
const expectedPartialAmount = 0;
|
||||
expect(partialAmount).to.be.bignumber.equal(expectedPartialAmount);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return true if there is a rounding error > 0.1%', async () => {
|
||||
const numerator = new BigNumber(3);
|
||||
const denominator = new BigNumber(7);
|
||||
const target = new BigNumber(10);
|
||||
// rounding error = ((3*10/7) - floor(3*10/7)) / (3*10/7) = 6.67%
|
||||
const isRoundingError = await exchangeWrapper.isRoundingErrorAsync(numerator, denominator, target);
|
||||
expect(isRoundingError).to.be.true();
|
||||
});
|
||||
|
||||
it('should return false when there is no rounding error', async () => {
|
||||
const numerator = new BigNumber(1);
|
||||
const denominator = new BigNumber(2);
|
||||
const target = new BigNumber(10);
|
||||
|
||||
const isRoundingError = await exchangeWrapper.isRoundingErrorAsync(numerator, denominator, target);
|
||||
expect(isRoundingError).to.be.false();
|
||||
});
|
||||
|
||||
it('should return false when there is rounding error <= 0.1%', async () => {
|
||||
// randomly generated numbers
|
||||
const numerator = new BigNumber(76564);
|
||||
const denominator = new BigNumber(676373677);
|
||||
const target = new BigNumber(105762562);
|
||||
// rounding error = ((76564*105762562/676373677) - floor(76564*105762562/676373677)) /
|
||||
// (76564*105762562/676373677) = 0.0007%
|
||||
const isRoundingError = await exchangeWrapper.isRoundingErrorAsync(numerator, denominator, target);
|
||||
expect(isRoundingError).to.be.false();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getPartialAmount', () => {
|
||||
it('should return the numerator/denominator*target', async () => {
|
||||
const numerator = new BigNumber(1);
|
||||
const denominator = new BigNumber(2);
|
||||
const target = new BigNumber(10);
|
||||
|
||||
const partialAmount = await exchangeWrapper.getPartialAmountAsync(numerator, denominator, target);
|
||||
const expectedPartialAmount = 5;
|
||||
expect(partialAmount).to.be.bignumber.equal(expectedPartialAmount);
|
||||
});
|
||||
|
||||
it('should round down', async () => {
|
||||
const numerator = new BigNumber(2);
|
||||
const denominator = new BigNumber(3);
|
||||
const target = new BigNumber(10);
|
||||
|
||||
const partialAmount = await exchangeWrapper.getPartialAmountAsync(numerator, denominator, target);
|
||||
const expectedPartialAmount = 6;
|
||||
expect(partialAmount).to.be.bignumber.equal(expectedPartialAmount);
|
||||
});
|
||||
|
||||
it('should round .5 down', async () => {
|
||||
const numerator = new BigNumber(1);
|
||||
const denominator = new BigNumber(20);
|
||||
const target = new BigNumber(10);
|
||||
|
||||
const partialAmount = await exchangeWrapper.getPartialAmountAsync(numerator, denominator, target);
|
||||
const expectedPartialAmount = 0;
|
||||
expect(partialAmount).to.be.bignumber.equal(expectedPartialAmount);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,303 +15,307 @@ import {chaiSetup} from '../utils/chai_setup';
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
const {
|
||||
Exchange,
|
||||
TokenTransferProxy,
|
||||
DummyToken,
|
||||
TokenRegistry,
|
||||
Exchange,
|
||||
TokenTransferProxy,
|
||||
DummyToken,
|
||||
TokenRegistry,
|
||||
} = new Artifacts(artifacts);
|
||||
|
||||
contract('Exchange', (accounts: string[]) => {
|
||||
const maker = accounts[0];
|
||||
const tokenOwner = accounts[0];
|
||||
const taker = accounts[1] || accounts[accounts.length - 1];
|
||||
const feeRecipient = accounts[2] || accounts[accounts.length - 1];
|
||||
const maker = accounts[0];
|
||||
const tokenOwner = accounts[0];
|
||||
const taker = accounts[1] || accounts[accounts.length - 1];
|
||||
const feeRecipient = accounts[2] || accounts[accounts.length - 1];
|
||||
|
||||
const INIT_BAL = ZeroEx.toBaseUnitAmount(new BigNumber(10000), 18);
|
||||
const INIT_ALLOW = ZeroEx.toBaseUnitAmount(new BigNumber(10000), 18);
|
||||
const INIT_BAL = ZeroEx.toBaseUnitAmount(new BigNumber(10000), 18);
|
||||
const INIT_ALLOW = ZeroEx.toBaseUnitAmount(new BigNumber(10000), 18);
|
||||
|
||||
let rep: ContractInstance;
|
||||
let dgd: ContractInstance;
|
||||
let zrx: ContractInstance;
|
||||
let exchange: ContractInstance;
|
||||
let tokenRegistry: ContractInstance;
|
||||
let rep: ContractInstance;
|
||||
let dgd: ContractInstance;
|
||||
let zrx: ContractInstance;
|
||||
let exchange: ContractInstance;
|
||||
let tokenRegistry: ContractInstance;
|
||||
|
||||
let balances: BalancesByOwner;
|
||||
let balances: BalancesByOwner;
|
||||
|
||||
let exWrapper: ExchangeWrapper;
|
||||
let dmyBalances: Balances;
|
||||
let orderFactory: OrderFactory;
|
||||
let exWrapper: ExchangeWrapper;
|
||||
let dmyBalances: Balances;
|
||||
let orderFactory: OrderFactory;
|
||||
|
||||
before(async () => {
|
||||
[tokenRegistry, exchange] = await Promise.all([
|
||||
TokenRegistry.deployed(),
|
||||
Exchange.deployed(),
|
||||
]);
|
||||
exWrapper = new ExchangeWrapper(exchange);
|
||||
const [repAddress, dgdAddress, zrxAddress] = await Promise.all([
|
||||
tokenRegistry.getTokenAddressBySymbol('REP'),
|
||||
tokenRegistry.getTokenAddressBySymbol('DGD'),
|
||||
tokenRegistry.getTokenAddressBySymbol('ZRX'),
|
||||
]);
|
||||
|
||||
const defaultOrderParams = {
|
||||
exchangeContractAddress: Exchange.address,
|
||||
maker,
|
||||
feeRecipient,
|
||||
makerToken: repAddress,
|
||||
takerToken: dgdAddress,
|
||||
makerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
|
||||
takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18),
|
||||
makerFee: ZeroEx.toBaseUnitAmount(new BigNumber(1), 18),
|
||||
takerFee: ZeroEx.toBaseUnitAmount(new BigNumber(1), 18),
|
||||
};
|
||||
orderFactory = new OrderFactory(defaultOrderParams);
|
||||
|
||||
[rep, dgd, zrx] = await Promise.all([
|
||||
DummyToken.at(repAddress),
|
||||
DummyToken.at(dgdAddress),
|
||||
DummyToken.at(zrxAddress),
|
||||
]);
|
||||
dmyBalances = new Balances([rep, dgd, zrx], [maker, taker, feeRecipient]);
|
||||
await Promise.all([
|
||||
rep.approve(TokenTransferProxy.address, INIT_ALLOW, {from: maker}),
|
||||
rep.approve(TokenTransferProxy.address, INIT_ALLOW, {from: taker}),
|
||||
rep.setBalance(maker, INIT_BAL, {from: tokenOwner}),
|
||||
rep.setBalance(taker, INIT_BAL, {from: tokenOwner}),
|
||||
dgd.approve(TokenTransferProxy.address, INIT_ALLOW, {from: maker}),
|
||||
dgd.approve(TokenTransferProxy.address, INIT_ALLOW, {from: taker}),
|
||||
dgd.setBalance(maker, INIT_BAL, {from: tokenOwner}),
|
||||
dgd.setBalance(taker, INIT_BAL, {from: tokenOwner}),
|
||||
zrx.approve(TokenTransferProxy.address, INIT_ALLOW, {from: maker}),
|
||||
zrx.approve(TokenTransferProxy.address, INIT_ALLOW, {from: taker}),
|
||||
zrx.setBalance(maker, INIT_BAL, {from: tokenOwner}),
|
||||
zrx.setBalance(taker, INIT_BAL, {from: tokenOwner}),
|
||||
]);
|
||||
});
|
||||
|
||||
describe('fillOrKillOrder', () => {
|
||||
beforeEach(async () => {
|
||||
balances = await dmyBalances.getAsync();
|
||||
});
|
||||
|
||||
it('should transfer the correct amounts', async () => {
|
||||
const order = await orderFactory.newSignedOrderAsync({
|
||||
makerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
|
||||
takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18),
|
||||
});
|
||||
const fillTakerTokenAmount = order.params.takerTokenAmount.div(2);
|
||||
await exWrapper.fillOrKillOrderAsync(order, taker, {fillTakerTokenAmount});
|
||||
|
||||
const newBalances = await dmyBalances.getAsync();
|
||||
|
||||
const fillMakerTokenAmount = fillTakerTokenAmount
|
||||
.times(order.params.makerTokenAmount)
|
||||
.dividedToIntegerBy(order.params.takerTokenAmount);
|
||||
const makerFee = order.params.makerFee
|
||||
.times(fillMakerTokenAmount)
|
||||
.dividedToIntegerBy(order.params.makerTokenAmount);
|
||||
const takerFee = order.params.takerFee
|
||||
.times(fillMakerTokenAmount)
|
||||
.dividedToIntegerBy(order.params.makerTokenAmount);
|
||||
expect(newBalances[maker][order.params.makerToken])
|
||||
.to.be.bignumber.equal(balances[maker][order.params.makerToken].minus(fillMakerTokenAmount));
|
||||
expect(newBalances[maker][order.params.takerToken])
|
||||
.to.be.bignumber.equal(balances[maker][order.params.takerToken].add(fillTakerTokenAmount));
|
||||
expect(newBalances[maker][zrx.address]).to.be.bignumber.equal(balances[maker][zrx.address].minus(makerFee));
|
||||
expect(newBalances[taker][order.params.takerToken])
|
||||
.to.be.bignumber.equal(balances[taker][order.params.takerToken].minus(fillTakerTokenAmount));
|
||||
expect(newBalances[taker][order.params.makerToken])
|
||||
.to.be.bignumber.equal(balances[taker][order.params.makerToken].add(fillMakerTokenAmount));
|
||||
expect(newBalances[taker][zrx.address]).to.be.bignumber.equal(balances[taker][zrx.address].minus(takerFee));
|
||||
expect(newBalances[feeRecipient][zrx.address])
|
||||
.to.be.bignumber.equal(balances[feeRecipient][zrx.address].add(makerFee.add(takerFee)));
|
||||
});
|
||||
|
||||
it('should throw if an order is expired', async () => {
|
||||
const order = await orderFactory.newSignedOrderAsync({
|
||||
expirationTimestampInSec: new BigNumber(Math.floor((Date.now() - 10000) / 1000)),
|
||||
});
|
||||
|
||||
return expect(exWrapper.fillOrKillOrderAsync(order, taker))
|
||||
.to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
|
||||
it('should throw if entire fillTakerTokenAmount not filled', async () => {
|
||||
const order = await orderFactory.newSignedOrderAsync();
|
||||
|
||||
const from = taker;
|
||||
await exWrapper.fillOrderAsync(order, from, {fillTakerTokenAmount: order.params.takerTokenAmount.div(2)});
|
||||
|
||||
return expect(exWrapper.fillOrKillOrderAsync(order, taker))
|
||||
.to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
});
|
||||
|
||||
describe('batch functions', () => {
|
||||
let orders: Order[];
|
||||
beforeEach(async () => {
|
||||
orders = await Promise.all([
|
||||
orderFactory.newSignedOrderAsync(),
|
||||
orderFactory.newSignedOrderAsync(),
|
||||
orderFactory.newSignedOrderAsync(),
|
||||
]);
|
||||
balances = await dmyBalances.getAsync();
|
||||
});
|
||||
|
||||
describe('batchFillOrders', () => {
|
||||
it('should transfer the correct amounts', async () => {
|
||||
const fillTakerTokenAmounts: BigNumber[] = [];
|
||||
const makerToken = rep.address;
|
||||
const takerToken = dgd.address;
|
||||
orders.forEach(order => {
|
||||
const fillTakerTokenAmount = order.params.takerTokenAmount.div(2);
|
||||
const fillMakerTokenAmount = fillTakerTokenAmount
|
||||
.times(order.params.makerTokenAmount)
|
||||
.dividedToIntegerBy(order.params.takerTokenAmount);
|
||||
const makerFee = order.params.makerFee
|
||||
.times(fillMakerTokenAmount)
|
||||
.dividedToIntegerBy(order.params.makerTokenAmount);
|
||||
const takerFee = order.params.takerFee
|
||||
.times(fillMakerTokenAmount)
|
||||
.dividedToIntegerBy(order.params.makerTokenAmount);
|
||||
fillTakerTokenAmounts.push(fillTakerTokenAmount);
|
||||
balances[maker][makerToken] = balances[maker][makerToken].minus(fillMakerTokenAmount);
|
||||
balances[maker][takerToken] = balances[maker][takerToken].add(fillTakerTokenAmount);
|
||||
balances[maker][zrx.address] = balances[maker][zrx.address].minus(makerFee);
|
||||
balances[taker][makerToken] = balances[taker][makerToken].add(fillMakerTokenAmount);
|
||||
balances[taker][takerToken] = balances[taker][takerToken].minus(fillTakerTokenAmount);
|
||||
balances[taker][zrx.address] = balances[taker][zrx.address].minus(takerFee);
|
||||
balances[feeRecipient][zrx.address] = balances[feeRecipient][zrx.address].add(makerFee.add(takerFee));
|
||||
});
|
||||
|
||||
await exWrapper.batchFillOrdersAsync(orders, taker, {fillTakerTokenAmounts});
|
||||
|
||||
const newBalances = await dmyBalances.getAsync();
|
||||
expect(newBalances).to.be.deep.equal(balances);
|
||||
});
|
||||
});
|
||||
|
||||
describe('batchFillOrKillOrders', () => {
|
||||
it('should transfer the correct amounts', async () => {
|
||||
const fillTakerTokenAmounts: BigNumber[] = [];
|
||||
const makerToken = rep.address;
|
||||
const takerToken = dgd.address;
|
||||
orders.forEach(order => {
|
||||
const fillTakerTokenAmount = order.params.takerTokenAmount.div(2);
|
||||
const fillMakerTokenAmount = fillTakerTokenAmount
|
||||
.times(order.params.makerTokenAmount)
|
||||
.dividedToIntegerBy(order.params.takerTokenAmount);
|
||||
const makerFee = order.params.makerFee
|
||||
.times(fillMakerTokenAmount)
|
||||
.dividedToIntegerBy(order.params.makerTokenAmount);
|
||||
const takerFee = order.params.takerFee
|
||||
.times(fillMakerTokenAmount)
|
||||
.dividedToIntegerBy(order.params.makerTokenAmount);
|
||||
fillTakerTokenAmounts.push(fillTakerTokenAmount);
|
||||
balances[maker][makerToken] = balances[maker][makerToken].minus(fillMakerTokenAmount);
|
||||
balances[maker][takerToken] = balances[maker][takerToken].add(fillTakerTokenAmount);
|
||||
balances[maker][zrx.address] = balances[maker][zrx.address].minus(makerFee);
|
||||
balances[taker][makerToken] = balances[taker][makerToken].add(fillMakerTokenAmount);
|
||||
balances[taker][takerToken] = balances[taker][takerToken].minus(fillTakerTokenAmount);
|
||||
balances[taker][zrx.address] = balances[taker][zrx.address].minus(takerFee);
|
||||
balances[feeRecipient][zrx.address] = balances[feeRecipient][zrx.address].add(makerFee.add(takerFee));
|
||||
});
|
||||
|
||||
await exWrapper.batchFillOrKillOrdersAsync(orders, taker, {fillTakerTokenAmounts});
|
||||
|
||||
const newBalances = await dmyBalances.getAsync();
|
||||
expect(newBalances).to.be.deep.equal(balances);
|
||||
});
|
||||
|
||||
it('should throw if a single order does not fill the expected amount', async () => {
|
||||
const fillTakerTokenAmounts: BigNumber[] = [];
|
||||
const makerToken = rep.address;
|
||||
const takerToken = dgd.address;
|
||||
orders.forEach(order => {
|
||||
const fillTakerTokenAmount = order.params.takerTokenAmount.div(2);
|
||||
fillTakerTokenAmounts.push(fillTakerTokenAmount);
|
||||
});
|
||||
|
||||
await exWrapper.fillOrKillOrderAsync(orders[0], taker);
|
||||
|
||||
return expect(exWrapper.batchFillOrKillOrdersAsync(orders, taker, {fillTakerTokenAmounts}))
|
||||
.to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
});
|
||||
|
||||
describe('fillOrdersUpTo', () => {
|
||||
it('should stop when the entire fillTakerTokenAmount is filled', async () => {
|
||||
const fillTakerTokenAmount = orders[0].params.takerTokenAmount.plus(orders[1].params.takerTokenAmount.div(2));
|
||||
await exWrapper.fillOrdersUpToAsync(orders, taker, {fillTakerTokenAmount});
|
||||
|
||||
const newBalances = await dmyBalances.getAsync();
|
||||
|
||||
const fillMakerTokenAmount = orders[0].params.makerTokenAmount.add(
|
||||
orders[1].params.makerTokenAmount.dividedToIntegerBy(2));
|
||||
const makerFee = orders[0].params.makerFee.add(orders[1].params.makerFee.dividedToIntegerBy(2));
|
||||
const takerFee = orders[0].params.takerFee.add(orders[1].params.takerFee.dividedToIntegerBy(2));
|
||||
expect(newBalances[maker][orders[0].params.makerToken])
|
||||
.to.be.bignumber.equal(balances[maker][orders[0].params.makerToken].minus(fillMakerTokenAmount));
|
||||
expect(newBalances[maker][orders[0].params.takerToken])
|
||||
.to.be.bignumber.equal(balances[maker][orders[0].params.takerToken].add(fillTakerTokenAmount));
|
||||
expect(newBalances[maker][zrx.address]).to.be.bignumber.equal(balances[maker][zrx.address].minus(makerFee));
|
||||
expect(newBalances[taker][orders[0].params.takerToken])
|
||||
.to.be.bignumber.equal(balances[taker][orders[0].params.takerToken].minus(fillTakerTokenAmount));
|
||||
expect(newBalances[taker][orders[0].params.makerToken])
|
||||
.to.be.bignumber.equal(balances[taker][orders[0].params.makerToken].add(fillMakerTokenAmount));
|
||||
expect(newBalances[taker][zrx.address]).to.be.bignumber.equal(balances[taker][zrx.address].minus(takerFee));
|
||||
expect(newBalances[feeRecipient][zrx.address])
|
||||
.to.be.bignumber.equal(balances[feeRecipient][zrx.address].add(makerFee.add(takerFee)));
|
||||
});
|
||||
|
||||
it('should fill all orders if cannot fill entire fillTakerTokenAmount', async () => {
|
||||
const fillTakerTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(100000), 18);
|
||||
orders.forEach(order => {
|
||||
balances[maker][order.params.makerToken] = balances[maker][order.params.makerToken]
|
||||
.minus(order.params.makerTokenAmount);
|
||||
balances[maker][order.params.takerToken] = balances[maker][order.params.takerToken]
|
||||
.add(order.params.takerTokenAmount);
|
||||
balances[maker][zrx.address] = balances[maker][zrx.address]
|
||||
.minus(order.params.makerFee);
|
||||
balances[taker][order.params.makerToken] = balances[taker][order.params.makerToken]
|
||||
.add(order.params.makerTokenAmount);
|
||||
balances[taker][order.params.takerToken] = balances[taker][order.params.takerToken]
|
||||
.minus(order.params.takerTokenAmount);
|
||||
balances[taker][zrx.address] = balances[taker][zrx.address].minus(order.params.takerFee);
|
||||
balances[feeRecipient][zrx.address] = balances[feeRecipient][zrx.address]
|
||||
.add(order.params.makerFee
|
||||
.add(order.params.takerFee));
|
||||
});
|
||||
await exWrapper.fillOrdersUpToAsync(orders, taker, {fillTakerTokenAmount});
|
||||
|
||||
const newBalances = await dmyBalances.getAsync();
|
||||
expect(newBalances).to.be.deep.equal(balances);
|
||||
});
|
||||
|
||||
it('should throw when an order does not use the same takerToken', async () => {
|
||||
orders = await Promise.all([
|
||||
orderFactory.newSignedOrderAsync(),
|
||||
orderFactory.newSignedOrderAsync({takerToken: zrx.address}),
|
||||
orderFactory.newSignedOrderAsync(),
|
||||
before(async () => {
|
||||
[tokenRegistry, exchange] = await Promise.all([
|
||||
TokenRegistry.deployed(),
|
||||
Exchange.deployed(),
|
||||
]);
|
||||
exWrapper = new ExchangeWrapper(exchange);
|
||||
const [repAddress, dgdAddress, zrxAddress] = await Promise.all([
|
||||
tokenRegistry.getTokenAddressBySymbol('REP'),
|
||||
tokenRegistry.getTokenAddressBySymbol('DGD'),
|
||||
tokenRegistry.getTokenAddressBySymbol('ZRX'),
|
||||
]);
|
||||
|
||||
return expect(
|
||||
exWrapper.fillOrdersUpToAsync(
|
||||
orders, taker, {fillTakerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(1000), 18)}),
|
||||
).to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
const defaultOrderParams = {
|
||||
exchangeContractAddress: Exchange.address,
|
||||
maker,
|
||||
feeRecipient,
|
||||
makerToken: repAddress,
|
||||
takerToken: dgdAddress,
|
||||
makerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
|
||||
takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18),
|
||||
makerFee: ZeroEx.toBaseUnitAmount(new BigNumber(1), 18),
|
||||
takerFee: ZeroEx.toBaseUnitAmount(new BigNumber(1), 18),
|
||||
};
|
||||
orderFactory = new OrderFactory(defaultOrderParams);
|
||||
|
||||
[rep, dgd, zrx] = await Promise.all([
|
||||
DummyToken.at(repAddress),
|
||||
DummyToken.at(dgdAddress),
|
||||
DummyToken.at(zrxAddress),
|
||||
]);
|
||||
dmyBalances = new Balances([rep, dgd, zrx], [maker, taker, feeRecipient]);
|
||||
await Promise.all([
|
||||
rep.approve(TokenTransferProxy.address, INIT_ALLOW, {from: maker}),
|
||||
rep.approve(TokenTransferProxy.address, INIT_ALLOW, {from: taker}),
|
||||
rep.setBalance(maker, INIT_BAL, {from: tokenOwner}),
|
||||
rep.setBalance(taker, INIT_BAL, {from: tokenOwner}),
|
||||
dgd.approve(TokenTransferProxy.address, INIT_ALLOW, {from: maker}),
|
||||
dgd.approve(TokenTransferProxy.address, INIT_ALLOW, {from: taker}),
|
||||
dgd.setBalance(maker, INIT_BAL, {from: tokenOwner}),
|
||||
dgd.setBalance(taker, INIT_BAL, {from: tokenOwner}),
|
||||
zrx.approve(TokenTransferProxy.address, INIT_ALLOW, {from: maker}),
|
||||
zrx.approve(TokenTransferProxy.address, INIT_ALLOW, {from: taker}),
|
||||
zrx.setBalance(maker, INIT_BAL, {from: tokenOwner}),
|
||||
zrx.setBalance(taker, INIT_BAL, {from: tokenOwner}),
|
||||
]);
|
||||
});
|
||||
|
||||
describe('batchCancelOrders', () => {
|
||||
it('should be able to cancel multiple orders', async () => {
|
||||
const cancelTakerTokenAmounts = _.map(orders, order => order.params.takerTokenAmount);
|
||||
await exWrapper.batchCancelOrdersAsync(orders, maker, {cancelTakerTokenAmounts});
|
||||
describe('fillOrKillOrder', () => {
|
||||
beforeEach(async () => {
|
||||
balances = await dmyBalances.getAsync();
|
||||
});
|
||||
|
||||
const res = await exWrapper.batchFillOrdersAsync(
|
||||
orders, taker, {fillTakerTokenAmounts: cancelTakerTokenAmounts});
|
||||
const newBalances = await dmyBalances.getAsync();
|
||||
expect(balances).to.be.deep.equal(newBalances);
|
||||
});
|
||||
it('should transfer the correct amounts', async () => {
|
||||
const order = await orderFactory.newSignedOrderAsync({
|
||||
makerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
|
||||
takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18),
|
||||
});
|
||||
const fillTakerTokenAmount = order.params.takerTokenAmount.div(2);
|
||||
await exWrapper.fillOrKillOrderAsync(order, taker, {fillTakerTokenAmount});
|
||||
|
||||
const newBalances = await dmyBalances.getAsync();
|
||||
|
||||
const fillMakerTokenAmount = fillTakerTokenAmount
|
||||
.times(order.params.makerTokenAmount)
|
||||
.dividedToIntegerBy(order.params.takerTokenAmount);
|
||||
const makerFee = order.params.makerFee
|
||||
.times(fillMakerTokenAmount)
|
||||
.dividedToIntegerBy(order.params.makerTokenAmount);
|
||||
const takerFee = order.params.takerFee
|
||||
.times(fillMakerTokenAmount)
|
||||
.dividedToIntegerBy(order.params.makerTokenAmount);
|
||||
expect(newBalances[maker][order.params.makerToken])
|
||||
.to.be.bignumber.equal(balances[maker][order.params.makerToken].minus(fillMakerTokenAmount));
|
||||
expect(newBalances[maker][order.params.takerToken])
|
||||
.to.be.bignumber.equal(balances[maker][order.params.takerToken].add(fillTakerTokenAmount));
|
||||
expect(newBalances[maker][zrx.address]).to.be.bignumber.equal(balances[maker][zrx.address].minus(makerFee));
|
||||
expect(newBalances[taker][order.params.takerToken])
|
||||
.to.be.bignumber.equal(balances[taker][order.params.takerToken].minus(fillTakerTokenAmount));
|
||||
expect(newBalances[taker][order.params.makerToken])
|
||||
.to.be.bignumber.equal(balances[taker][order.params.makerToken].add(fillMakerTokenAmount));
|
||||
expect(newBalances[taker][zrx.address]).to.be.bignumber.equal(balances[taker][zrx.address].minus(takerFee));
|
||||
expect(newBalances[feeRecipient][zrx.address])
|
||||
.to.be.bignumber.equal(balances[feeRecipient][zrx.address].add(makerFee.add(takerFee)));
|
||||
});
|
||||
|
||||
it('should throw if an order is expired', async () => {
|
||||
const order = await orderFactory.newSignedOrderAsync({
|
||||
expirationTimestampInSec: new BigNumber(Math.floor((Date.now() - 10000) / 1000)),
|
||||
});
|
||||
|
||||
return expect(exWrapper.fillOrKillOrderAsync(order, taker))
|
||||
.to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
|
||||
it('should throw if entire fillTakerTokenAmount not filled', async () => {
|
||||
const order = await orderFactory.newSignedOrderAsync();
|
||||
|
||||
const from = taker;
|
||||
await exWrapper.fillOrderAsync(order, from, {fillTakerTokenAmount: order.params.takerTokenAmount.div(2)});
|
||||
|
||||
return expect(exWrapper.fillOrKillOrderAsync(order, taker))
|
||||
.to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
});
|
||||
|
||||
describe('batch functions', () => {
|
||||
let orders: Order[];
|
||||
beforeEach(async () => {
|
||||
orders = await Promise.all([
|
||||
orderFactory.newSignedOrderAsync(),
|
||||
orderFactory.newSignedOrderAsync(),
|
||||
orderFactory.newSignedOrderAsync(),
|
||||
]);
|
||||
balances = await dmyBalances.getAsync();
|
||||
});
|
||||
|
||||
describe('batchFillOrders', () => {
|
||||
it('should transfer the correct amounts', async () => {
|
||||
const fillTakerTokenAmounts: BigNumber[] = [];
|
||||
const makerToken = rep.address;
|
||||
const takerToken = dgd.address;
|
||||
orders.forEach(order => {
|
||||
const fillTakerTokenAmount = order.params.takerTokenAmount.div(2);
|
||||
const fillMakerTokenAmount = fillTakerTokenAmount
|
||||
.times(order.params.makerTokenAmount)
|
||||
.dividedToIntegerBy(order.params.takerTokenAmount);
|
||||
const makerFee = order.params.makerFee
|
||||
.times(fillMakerTokenAmount)
|
||||
.dividedToIntegerBy(order.params.makerTokenAmount);
|
||||
const takerFee = order.params.takerFee
|
||||
.times(fillMakerTokenAmount)
|
||||
.dividedToIntegerBy(order.params.makerTokenAmount);
|
||||
fillTakerTokenAmounts.push(fillTakerTokenAmount);
|
||||
balances[maker][makerToken] = balances[maker][makerToken].minus(fillMakerTokenAmount);
|
||||
balances[maker][takerToken] = balances[maker][takerToken].add(fillTakerTokenAmount);
|
||||
balances[maker][zrx.address] = balances[maker][zrx.address].minus(makerFee);
|
||||
balances[taker][makerToken] = balances[taker][makerToken].add(fillMakerTokenAmount);
|
||||
balances[taker][takerToken] = balances[taker][takerToken].minus(fillTakerTokenAmount);
|
||||
balances[taker][zrx.address] = balances[taker][zrx.address].minus(takerFee);
|
||||
balances[feeRecipient][zrx.address] =
|
||||
balances[feeRecipient][zrx.address].add(makerFee.add(takerFee));
|
||||
});
|
||||
|
||||
await exWrapper.batchFillOrdersAsync(orders, taker, {fillTakerTokenAmounts});
|
||||
|
||||
const newBalances = await dmyBalances.getAsync();
|
||||
expect(newBalances).to.be.deep.equal(balances);
|
||||
});
|
||||
});
|
||||
|
||||
describe('batchFillOrKillOrders', () => {
|
||||
it('should transfer the correct amounts', async () => {
|
||||
const fillTakerTokenAmounts: BigNumber[] = [];
|
||||
const makerToken = rep.address;
|
||||
const takerToken = dgd.address;
|
||||
orders.forEach(order => {
|
||||
const fillTakerTokenAmount = order.params.takerTokenAmount.div(2);
|
||||
const fillMakerTokenAmount = fillTakerTokenAmount
|
||||
.times(order.params.makerTokenAmount)
|
||||
.dividedToIntegerBy(order.params.takerTokenAmount);
|
||||
const makerFee = order.params.makerFee
|
||||
.times(fillMakerTokenAmount)
|
||||
.dividedToIntegerBy(order.params.makerTokenAmount);
|
||||
const takerFee = order.params.takerFee
|
||||
.times(fillMakerTokenAmount)
|
||||
.dividedToIntegerBy(order.params.makerTokenAmount);
|
||||
fillTakerTokenAmounts.push(fillTakerTokenAmount);
|
||||
balances[maker][makerToken] = balances[maker][makerToken].minus(fillMakerTokenAmount);
|
||||
balances[maker][takerToken] = balances[maker][takerToken].add(fillTakerTokenAmount);
|
||||
balances[maker][zrx.address] = balances[maker][zrx.address].minus(makerFee);
|
||||
balances[taker][makerToken] = balances[taker][makerToken].add(fillMakerTokenAmount);
|
||||
balances[taker][takerToken] = balances[taker][takerToken].minus(fillTakerTokenAmount);
|
||||
balances[taker][zrx.address] = balances[taker][zrx.address].minus(takerFee);
|
||||
balances[feeRecipient][zrx.address] =
|
||||
balances[feeRecipient][zrx.address].add(makerFee.add(takerFee));
|
||||
});
|
||||
|
||||
await exWrapper.batchFillOrKillOrdersAsync(orders, taker, {fillTakerTokenAmounts});
|
||||
|
||||
const newBalances = await dmyBalances.getAsync();
|
||||
expect(newBalances).to.be.deep.equal(balances);
|
||||
});
|
||||
|
||||
it('should throw if a single order does not fill the expected amount', async () => {
|
||||
const fillTakerTokenAmounts: BigNumber[] = [];
|
||||
const makerToken = rep.address;
|
||||
const takerToken = dgd.address;
|
||||
orders.forEach(order => {
|
||||
const fillTakerTokenAmount = order.params.takerTokenAmount.div(2);
|
||||
fillTakerTokenAmounts.push(fillTakerTokenAmount);
|
||||
});
|
||||
|
||||
await exWrapper.fillOrKillOrderAsync(orders[0], taker);
|
||||
|
||||
return expect(exWrapper.batchFillOrKillOrdersAsync(orders, taker, {fillTakerTokenAmounts}))
|
||||
.to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
});
|
||||
|
||||
describe('fillOrdersUpTo', () => {
|
||||
it('should stop when the entire fillTakerTokenAmount is filled', async () => {
|
||||
const fillTakerTokenAmount =
|
||||
orders[0].params.takerTokenAmount.plus(orders[1].params.takerTokenAmount.div(2));
|
||||
await exWrapper.fillOrdersUpToAsync(orders, taker, {fillTakerTokenAmount});
|
||||
|
||||
const newBalances = await dmyBalances.getAsync();
|
||||
|
||||
const fillMakerTokenAmount = orders[0].params.makerTokenAmount.add(
|
||||
orders[1].params.makerTokenAmount.dividedToIntegerBy(2));
|
||||
const makerFee = orders[0].params.makerFee.add(orders[1].params.makerFee.dividedToIntegerBy(2));
|
||||
const takerFee = orders[0].params.takerFee.add(orders[1].params.takerFee.dividedToIntegerBy(2));
|
||||
expect(newBalances[maker][orders[0].params.makerToken])
|
||||
.to.be.bignumber.equal(balances[maker][orders[0].params.makerToken].minus(fillMakerTokenAmount));
|
||||
expect(newBalances[maker][orders[0].params.takerToken])
|
||||
.to.be.bignumber.equal(balances[maker][orders[0].params.takerToken].add(fillTakerTokenAmount));
|
||||
expect(newBalances[maker][zrx.address]).to.be.bignumber
|
||||
.equal(balances[maker][zrx.address].minus(makerFee));
|
||||
expect(newBalances[taker][orders[0].params.takerToken])
|
||||
.to.be.bignumber.equal(balances[taker][orders[0].params.takerToken].minus(fillTakerTokenAmount));
|
||||
expect(newBalances[taker][orders[0].params.makerToken])
|
||||
.to.be.bignumber.equal(balances[taker][orders[0].params.makerToken].add(fillMakerTokenAmount));
|
||||
expect(newBalances[taker][zrx.address]).to.be.bignumber
|
||||
.equal(balances[taker][zrx.address].minus(takerFee));
|
||||
expect(newBalances[feeRecipient][zrx.address])
|
||||
.to.be.bignumber.equal(balances[feeRecipient][zrx.address].add(makerFee.add(takerFee)));
|
||||
});
|
||||
|
||||
it('should fill all orders if cannot fill entire fillTakerTokenAmount', async () => {
|
||||
const fillTakerTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(100000), 18);
|
||||
orders.forEach(order => {
|
||||
balances[maker][order.params.makerToken] = balances[maker][order.params.makerToken]
|
||||
.minus(order.params.makerTokenAmount);
|
||||
balances[maker][order.params.takerToken] = balances[maker][order.params.takerToken]
|
||||
.add(order.params.takerTokenAmount);
|
||||
balances[maker][zrx.address] = balances[maker][zrx.address]
|
||||
.minus(order.params.makerFee);
|
||||
balances[taker][order.params.makerToken] = balances[taker][order.params.makerToken]
|
||||
.add(order.params.makerTokenAmount);
|
||||
balances[taker][order.params.takerToken] = balances[taker][order.params.takerToken]
|
||||
.minus(order.params.takerTokenAmount);
|
||||
balances[taker][zrx.address] = balances[taker][zrx.address].minus(order.params.takerFee);
|
||||
balances[feeRecipient][zrx.address] =
|
||||
balances[feeRecipient][zrx.address].add(order.params.makerFee.add(order.params.takerFee));
|
||||
});
|
||||
await exWrapper.fillOrdersUpToAsync(orders, taker, {fillTakerTokenAmount});
|
||||
|
||||
const newBalances = await dmyBalances.getAsync();
|
||||
expect(newBalances).to.be.deep.equal(balances);
|
||||
});
|
||||
|
||||
it('should throw when an order does not use the same takerToken', async () => {
|
||||
orders = await Promise.all([
|
||||
orderFactory.newSignedOrderAsync(),
|
||||
orderFactory.newSignedOrderAsync({takerToken: zrx.address}),
|
||||
orderFactory.newSignedOrderAsync(),
|
||||
]);
|
||||
|
||||
return expect(
|
||||
exWrapper.fillOrdersUpToAsync(
|
||||
orders, taker, {fillTakerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(1000), 18)}),
|
||||
).to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
});
|
||||
|
||||
describe('batchCancelOrders', () => {
|
||||
it('should be able to cancel multiple orders', async () => {
|
||||
const cancelTakerTokenAmounts = _.map(orders, order => order.params.takerTokenAmount);
|
||||
await exWrapper.batchCancelOrdersAsync(orders, maker, {cancelTakerTokenAmounts});
|
||||
|
||||
const res = await exWrapper.batchFillOrdersAsync(
|
||||
orders, taker, {fillTakerTokenAmounts: cancelTakerTokenAmounts});
|
||||
const newBalances = await dmyBalances.getAsync();
|
||||
expect(balances).to.be.deep.equal(newBalances);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -23,89 +23,89 @@ const expect = chai.expect;
|
||||
const web3: Web3 = (global as any).web3;
|
||||
|
||||
contract('MultiSigWalletWithTimeLock', (accounts: string[]) => {
|
||||
const owners = [accounts[0], accounts[1]];
|
||||
const SECONDS_TIME_LOCKED = 10000;
|
||||
const owners = [accounts[0], accounts[1]];
|
||||
const SECONDS_TIME_LOCKED = 10000;
|
||||
|
||||
let multiSig: ContractInstance;
|
||||
let multiSigWrapper: MultiSigWrapper;
|
||||
let txId: number;
|
||||
let initialSecondsTimeLocked: number;
|
||||
let rpc: RPC;
|
||||
let multiSig: ContractInstance;
|
||||
let multiSigWrapper: MultiSigWrapper;
|
||||
let txId: number;
|
||||
let initialSecondsTimeLocked: number;
|
||||
let rpc: RPC;
|
||||
|
||||
before(async () => {
|
||||
multiSig = await MultiSigWalletWithTimeLock.deployed();
|
||||
multiSigWrapper = new MultiSigWrapper(multiSig);
|
||||
before(async () => {
|
||||
multiSig = await MultiSigWalletWithTimeLock.deployed();
|
||||
multiSigWrapper = new MultiSigWrapper(multiSig);
|
||||
|
||||
const secondsTimeLocked = await multiSig.secondsTimeLocked.call();
|
||||
initialSecondsTimeLocked = secondsTimeLocked.toNumber();
|
||||
rpc = new RPC();
|
||||
});
|
||||
|
||||
describe('changeTimeLock', () => {
|
||||
it('should throw when not called by wallet', async () => {
|
||||
return expect(multiSig.changeTimeLock(SECONDS_TIME_LOCKED, {from: owners[0]}))
|
||||
.to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
const secondsTimeLocked = await multiSig.secondsTimeLocked.call();
|
||||
initialSecondsTimeLocked = secondsTimeLocked.toNumber();
|
||||
rpc = new RPC();
|
||||
});
|
||||
|
||||
it('should throw without enough confirmations', async () => {
|
||||
const destination = multiSig.address;
|
||||
const from = owners[0];
|
||||
const dataParams = {
|
||||
name: 'changeTimeLock',
|
||||
abi: MULTI_SIG_ABI,
|
||||
args: [SECONDS_TIME_LOCKED],
|
||||
};
|
||||
const subRes = await multiSigWrapper.submitTransactionAsync(destination, from, dataParams);
|
||||
describe('changeTimeLock', () => {
|
||||
it('should throw when not called by wallet', async () => {
|
||||
return expect(multiSig.changeTimeLock(SECONDS_TIME_LOCKED, {from: owners[0]}))
|
||||
.to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
|
||||
txId = subRes.logs[0].args.transactionId.toNumber();
|
||||
return expect(multiSig.executeTransaction(txId)).to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
it('should throw without enough confirmations', async () => {
|
||||
const destination = multiSig.address;
|
||||
const from = owners[0];
|
||||
const dataParams = {
|
||||
name: 'changeTimeLock',
|
||||
abi: MULTI_SIG_ABI,
|
||||
args: [SECONDS_TIME_LOCKED],
|
||||
};
|
||||
const subRes = await multiSigWrapper.submitTransactionAsync(destination, from, dataParams);
|
||||
|
||||
txId = subRes.logs[0].args.transactionId.toNumber();
|
||||
return expect(multiSig.executeTransaction(txId)).to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
|
||||
it('should set confirmation time with enough confirmations', async () => {
|
||||
const res = await multiSig.confirmTransaction(txId, {from: owners[1]});
|
||||
expect(res.logs).to.have.length(2);
|
||||
const blockNum = await promisify(web3.eth.getBlockNumber)();
|
||||
const blockInfo = await promisify(web3.eth.getBlock)(blockNum);
|
||||
const timestamp = new BigNumber(blockInfo.timestamp);
|
||||
const confirmationTimeBigNum = new BigNumber(await multiSig.confirmationTimes.call(txId));
|
||||
|
||||
expect(timestamp).to.be.bignumber.equal(confirmationTimeBigNum);
|
||||
});
|
||||
|
||||
it('should be executable with enough confirmations and secondsTimeLocked of 0', async () => {
|
||||
expect(initialSecondsTimeLocked).to.be.equal(0);
|
||||
|
||||
const res = await multiSig.executeTransaction(txId);
|
||||
expect(res.logs).to.have.length(2);
|
||||
|
||||
const secondsTimeLocked = new BigNumber(await multiSig.secondsTimeLocked.call());
|
||||
expect(secondsTimeLocked).to.be.bignumber.equal(SECONDS_TIME_LOCKED);
|
||||
});
|
||||
|
||||
const newSecondsTimeLocked = 0;
|
||||
it('should throw if it has enough confirmations but is not past the time lock', async () => {
|
||||
const destination = multiSig.address;
|
||||
const from = owners[0];
|
||||
const dataParams = {
|
||||
name: 'changeTimeLock',
|
||||
abi: MULTI_SIG_ABI,
|
||||
args: [newSecondsTimeLocked],
|
||||
};
|
||||
const subRes = await multiSigWrapper.submitTransactionAsync(destination, from, dataParams);
|
||||
|
||||
txId = subRes.logs[0].args.transactionId.toNumber();
|
||||
const confRes = await multiSig.confirmTransaction(txId, {from: owners[1]});
|
||||
expect(confRes.logs).to.have.length(2);
|
||||
|
||||
return expect(multiSig.executeTransaction(txId)).to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
|
||||
it('should execute if it has enough confirmations and is past the time lock', async () => {
|
||||
await rpc.increaseTimeAsync(SECONDS_TIME_LOCKED);
|
||||
await multiSig.executeTransaction(txId);
|
||||
|
||||
const secondsTimeLocked = new BigNumber(await multiSig.secondsTimeLocked.call());
|
||||
expect(secondsTimeLocked).to.be.bignumber.equal(newSecondsTimeLocked);
|
||||
});
|
||||
});
|
||||
|
||||
it('should set confirmation time with enough confirmations', async () => {
|
||||
const res = await multiSig.confirmTransaction(txId, {from: owners[1]});
|
||||
expect(res.logs).to.have.length(2);
|
||||
const blockNum = await promisify(web3.eth.getBlockNumber)();
|
||||
const blockInfo = await promisify(web3.eth.getBlock)(blockNum);
|
||||
const timestamp = new BigNumber(blockInfo.timestamp);
|
||||
const confirmationTimeBigNum = new BigNumber(await multiSig.confirmationTimes.call(txId));
|
||||
|
||||
expect(timestamp).to.be.bignumber.equal(confirmationTimeBigNum);
|
||||
});
|
||||
|
||||
it('should be executable with enough confirmations and secondsTimeLocked of 0', async () => {
|
||||
expect(initialSecondsTimeLocked).to.be.equal(0);
|
||||
|
||||
const res = await multiSig.executeTransaction(txId);
|
||||
expect(res.logs).to.have.length(2);
|
||||
|
||||
const secondsTimeLocked = new BigNumber(await multiSig.secondsTimeLocked.call());
|
||||
expect(secondsTimeLocked).to.be.bignumber.equal(SECONDS_TIME_LOCKED);
|
||||
});
|
||||
|
||||
const newSecondsTimeLocked = 0;
|
||||
it('should throw if it has enough confirmations but is not past the time lock', async () => {
|
||||
const destination = multiSig.address;
|
||||
const from = owners[0];
|
||||
const dataParams = {
|
||||
name: 'changeTimeLock',
|
||||
abi: MULTI_SIG_ABI,
|
||||
args: [newSecondsTimeLocked],
|
||||
};
|
||||
const subRes = await multiSigWrapper.submitTransactionAsync(destination, from, dataParams);
|
||||
|
||||
txId = subRes.logs[0].args.transactionId.toNumber();
|
||||
const confRes = await multiSig.confirmTransaction(txId, {from: owners[1]});
|
||||
expect(confRes.logs).to.have.length(2);
|
||||
|
||||
return expect(multiSig.executeTransaction(txId)).to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
|
||||
it('should execute if it has enough confirmations and is past the time lock', async () => {
|
||||
await rpc.increaseTimeAsync(SECONDS_TIME_LOCKED);
|
||||
await multiSig.executeTransaction(txId);
|
||||
|
||||
const secondsTimeLocked = new BigNumber(await multiSig.secondsTimeLocked.call());
|
||||
expect(secondsTimeLocked).to.be.bignumber.equal(newSecondsTimeLocked);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,122 +15,123 @@ chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
|
||||
contract('MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress', (accounts: string[]) => {
|
||||
const owners = [accounts[0], accounts[1]];
|
||||
const requiredApprovals = 2;
|
||||
const SECONDS_TIME_LOCKED = 1000000;
|
||||
const owners = [accounts[0], accounts[1]];
|
||||
const requiredApprovals = 2;
|
||||
const SECONDS_TIME_LOCKED = 1000000;
|
||||
|
||||
// initialize fake addresses
|
||||
const authorizedAddress = `0x${crypto.solSHA3([accounts[0]]).slice(0, 20).toString('hex')}`;
|
||||
const unauthorizedAddress = `0x${crypto.solSHA3([accounts[1]]).slice(0, 20).toString('hex')}`;
|
||||
// initialize fake addresses
|
||||
const authorizedAddress = `0x${crypto.solSHA3([accounts[0]]).slice(0, 20).toString('hex')}`;
|
||||
const unauthorizedAddress = `0x${crypto.solSHA3([accounts[1]]).slice(0, 20).toString('hex')}`;
|
||||
|
||||
let tokenTransferProxy: ContractInstance;
|
||||
let multiSig: ContractInstance;
|
||||
let multiSigWrapper: MultiSigWrapper;
|
||||
let tokenTransferProxy: ContractInstance;
|
||||
let multiSig: ContractInstance;
|
||||
let multiSigWrapper: MultiSigWrapper;
|
||||
|
||||
let validDestination: string;
|
||||
let validDestination: string;
|
||||
|
||||
beforeEach(async () => {
|
||||
const initialOwner = accounts[0];
|
||||
tokenTransferProxy = await TokenTransferProxy.new({from: initialOwner});
|
||||
await tokenTransferProxy.addAuthorizedAddress(authorizedAddress, {from: initialOwner});
|
||||
multiSig = await MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress.new(
|
||||
owners, requiredApprovals, SECONDS_TIME_LOCKED, tokenTransferProxy.address);
|
||||
await tokenTransferProxy.transferOwnership(multiSig.address, {from: initialOwner});
|
||||
multiSigWrapper = new MultiSigWrapper(multiSig);
|
||||
validDestination = tokenTransferProxy.address;
|
||||
});
|
||||
|
||||
describe('isFunctionRemoveAuthorizedAddress', () => {
|
||||
it('should throw if data is not for removeAuthorizedAddress', async () => {
|
||||
const data = multiSigWrapper.encodeFnArgs('addAuthorizedAddress', PROXY_ABI, [owners[0]]);
|
||||
return expect(multiSig.isFunctionRemoveAuthorizedAddress.call(data)).to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
beforeEach(async () => {
|
||||
const initialOwner = accounts[0];
|
||||
tokenTransferProxy = await TokenTransferProxy.new({from: initialOwner});
|
||||
await tokenTransferProxy.addAuthorizedAddress(authorizedAddress, {from: initialOwner});
|
||||
multiSig = await MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress.new(
|
||||
owners, requiredApprovals, SECONDS_TIME_LOCKED, tokenTransferProxy.address);
|
||||
await tokenTransferProxy.transferOwnership(multiSig.address, {from: initialOwner});
|
||||
multiSigWrapper = new MultiSigWrapper(multiSig);
|
||||
validDestination = tokenTransferProxy.address;
|
||||
});
|
||||
|
||||
it('should return true if data is for removeAuthorizedAddress', async () => {
|
||||
const data = multiSigWrapper.encodeFnArgs('removeAuthorizedAddress', PROXY_ABI, [owners[0]]);
|
||||
const isFunctionRemoveAuthorizedAddress = await multiSig.isFunctionRemoveAuthorizedAddress.call(data);
|
||||
expect(isFunctionRemoveAuthorizedAddress).to.be.true();
|
||||
});
|
||||
});
|
||||
describe('isFunctionRemoveAuthorizedAddress', () => {
|
||||
it('should throw if data is not for removeAuthorizedAddress', async () => {
|
||||
const data = multiSigWrapper.encodeFnArgs('addAuthorizedAddress', PROXY_ABI, [owners[0]]);
|
||||
return expect(multiSig.isFunctionRemoveAuthorizedAddress.call(data))
|
||||
.to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
|
||||
describe('executeRemoveAuthorizedAddress', () => {
|
||||
it('should throw without the required confirmations', async () => {
|
||||
const dataParams: TransactionDataParams = {
|
||||
name: 'removeAuthorizedAddress',
|
||||
abi: PROXY_ABI,
|
||||
args: [authorizedAddress],
|
||||
};
|
||||
const res = await multiSigWrapper.submitTransactionAsync(validDestination, owners[0], dataParams);
|
||||
const txId = res.logs[0].args.transactionId.toString();
|
||||
|
||||
return expect(multiSig.executeRemoveAuthorizedAddress(txId)).to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
it('should return true if data is for removeAuthorizedAddress', async () => {
|
||||
const data = multiSigWrapper.encodeFnArgs('removeAuthorizedAddress', PROXY_ABI, [owners[0]]);
|
||||
const isFunctionRemoveAuthorizedAddress = await multiSig.isFunctionRemoveAuthorizedAddress.call(data);
|
||||
expect(isFunctionRemoveAuthorizedAddress).to.be.true();
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw if tx destination is not the tokenTransferProxy', async () => {
|
||||
const invalidTokenTransferProxy = await TokenTransferProxy.new();
|
||||
const invalidDestination = invalidTokenTransferProxy.address;
|
||||
const dataParams: TransactionDataParams = {
|
||||
name: 'removeAuthorizedAddress',
|
||||
abi: PROXY_ABI,
|
||||
args: [authorizedAddress],
|
||||
};
|
||||
const res = await multiSigWrapper.submitTransactionAsync(invalidDestination, owners[0], dataParams);
|
||||
const txId = res.logs[0].args.transactionId.toString();
|
||||
await multiSig.confirmTransaction(txId, {from: owners[1]});
|
||||
const isConfirmed = await multiSig.isConfirmed.call(txId);
|
||||
expect(isConfirmed).to.be.true();
|
||||
describe('executeRemoveAuthorizedAddress', () => {
|
||||
it('should throw without the required confirmations', async () => {
|
||||
const dataParams: TransactionDataParams = {
|
||||
name: 'removeAuthorizedAddress',
|
||||
abi: PROXY_ABI,
|
||||
args: [authorizedAddress],
|
||||
};
|
||||
const res = await multiSigWrapper.submitTransactionAsync(validDestination, owners[0], dataParams);
|
||||
const txId = res.logs[0].args.transactionId.toString();
|
||||
|
||||
return expect(multiSig.executeRemoveAuthorizedAddress(txId)).to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
return expect(multiSig.executeRemoveAuthorizedAddress(txId)).to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
|
||||
it('should throw if tx destination is not the tokenTransferProxy', async () => {
|
||||
const invalidTokenTransferProxy = await TokenTransferProxy.new();
|
||||
const invalidDestination = invalidTokenTransferProxy.address;
|
||||
const dataParams: TransactionDataParams = {
|
||||
name: 'removeAuthorizedAddress',
|
||||
abi: PROXY_ABI,
|
||||
args: [authorizedAddress],
|
||||
};
|
||||
const res = await multiSigWrapper.submitTransactionAsync(invalidDestination, owners[0], dataParams);
|
||||
const txId = res.logs[0].args.transactionId.toString();
|
||||
await multiSig.confirmTransaction(txId, {from: owners[1]});
|
||||
const isConfirmed = await multiSig.isConfirmed.call(txId);
|
||||
expect(isConfirmed).to.be.true();
|
||||
|
||||
return expect(multiSig.executeRemoveAuthorizedAddress(txId)).to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
|
||||
it('should throw if tx data is not for removeAuthorizedAddress', async () => {
|
||||
const dataParams: TransactionDataParams = {
|
||||
name: 'addAuthorizedAddress',
|
||||
abi: PROXY_ABI,
|
||||
args: [unauthorizedAddress],
|
||||
};
|
||||
const res = await multiSigWrapper.submitTransactionAsync(validDestination, owners[0], dataParams);
|
||||
const txId = res.logs[0].args.transactionId.toString();
|
||||
await multiSig.confirmTransaction(txId, {from: owners[1]});
|
||||
const isConfirmed = await multiSig.isConfirmed.call(txId);
|
||||
expect(isConfirmed).to.be.true();
|
||||
|
||||
return expect(multiSig.executeRemoveAuthorizedAddress(txId)).to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
|
||||
it('should execute removeAuthorizedAddress for valid tokenTransferProxy if fully confirmed', async () => {
|
||||
const dataParams: TransactionDataParams = {
|
||||
name: 'removeAuthorizedAddress',
|
||||
abi: PROXY_ABI,
|
||||
args: [authorizedAddress],
|
||||
};
|
||||
const res = await multiSigWrapper.submitTransactionAsync(validDestination, owners[0], dataParams);
|
||||
const txId = res.logs[0].args.transactionId.toString();
|
||||
await multiSig.confirmTransaction(txId, {from: owners[1]});
|
||||
const isConfirmed = await multiSig.isConfirmed.call(txId);
|
||||
expect(isConfirmed).to.be.true();
|
||||
await multiSig.executeRemoveAuthorizedAddress(txId);
|
||||
|
||||
const isAuthorized = await tokenTransferProxy.authorized.call(authorizedAddress);
|
||||
expect(isAuthorized).to.be.false();
|
||||
});
|
||||
|
||||
it('should throw if already executed', async () => {
|
||||
const dataParams: TransactionDataParams = {
|
||||
name: 'removeAuthorizedAddress',
|
||||
abi: PROXY_ABI,
|
||||
args: [authorizedAddress],
|
||||
};
|
||||
const res = await multiSigWrapper.submitTransactionAsync(validDestination, owners[0], dataParams);
|
||||
const txId = res.logs[0].args.transactionId.toString();
|
||||
await multiSig.confirmTransaction(txId, {from: owners[1]});
|
||||
const isConfirmed = await multiSig.isConfirmed.call(txId);
|
||||
expect(isConfirmed).to.be.true();
|
||||
await multiSig.executeRemoveAuthorizedAddress(txId);
|
||||
const tx = await multiSig.transactions.call(txId);
|
||||
const isExecuted = tx[3];
|
||||
expect(isExecuted).to.be.true();
|
||||
return expect(multiSig.executeRemoveAuthorizedAddress(txId)).to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw if tx data is not for removeAuthorizedAddress', async () => {
|
||||
const dataParams: TransactionDataParams = {
|
||||
name: 'addAuthorizedAddress',
|
||||
abi: PROXY_ABI,
|
||||
args: [unauthorizedAddress],
|
||||
};
|
||||
const res = await multiSigWrapper.submitTransactionAsync(validDestination, owners[0], dataParams);
|
||||
const txId = res.logs[0].args.transactionId.toString();
|
||||
await multiSig.confirmTransaction(txId, {from: owners[1]});
|
||||
const isConfirmed = await multiSig.isConfirmed.call(txId);
|
||||
expect(isConfirmed).to.be.true();
|
||||
|
||||
return expect(multiSig.executeRemoveAuthorizedAddress(txId)).to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
|
||||
it('should execute removeAuthorizedAddress for valid tokenTransferProxy if fully confirmed', async () => {
|
||||
const dataParams: TransactionDataParams = {
|
||||
name: 'removeAuthorizedAddress',
|
||||
abi: PROXY_ABI,
|
||||
args: [authorizedAddress],
|
||||
};
|
||||
const res = await multiSigWrapper.submitTransactionAsync(validDestination, owners[0], dataParams);
|
||||
const txId = res.logs[0].args.transactionId.toString();
|
||||
await multiSig.confirmTransaction(txId, {from: owners[1]});
|
||||
const isConfirmed = await multiSig.isConfirmed.call(txId);
|
||||
expect(isConfirmed).to.be.true();
|
||||
await multiSig.executeRemoveAuthorizedAddress(txId);
|
||||
|
||||
const isAuthorized = await tokenTransferProxy.authorized.call(authorizedAddress);
|
||||
expect(isAuthorized).to.be.false();
|
||||
});
|
||||
|
||||
it('should throw if already executed', async () => {
|
||||
const dataParams: TransactionDataParams = {
|
||||
name: 'removeAuthorizedAddress',
|
||||
abi: PROXY_ABI,
|
||||
args: [authorizedAddress],
|
||||
};
|
||||
const res = await multiSigWrapper.submitTransactionAsync(validDestination, owners[0], dataParams);
|
||||
const txId = res.logs[0].args.transactionId.toString();
|
||||
await multiSig.confirmTransaction(txId, {from: owners[1]});
|
||||
const isConfirmed = await multiSig.isConfirmed.call(txId);
|
||||
expect(isConfirmed).to.be.true();
|
||||
await multiSig.executeRemoveAuthorizedAddress(txId);
|
||||
const tx = await multiSig.transactions.call(txId);
|
||||
const isExecuted = tx[3];
|
||||
expect(isExecuted).to.be.true();
|
||||
return expect(multiSig.executeRemoveAuthorizedAddress(txId)).to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,198 +15,198 @@ chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
|
||||
contract('TokenRegistry', (accounts: string[]) => {
|
||||
const owner = accounts[0];
|
||||
const notOwner = accounts[1];
|
||||
const owner = accounts[0];
|
||||
const notOwner = accounts[1];
|
||||
|
||||
const tokenAddress1 = `0x${ethUtil.setLength(ethUtil.toBuffer('0x1'), 20, false).toString('hex')}`;
|
||||
const tokenAddress2 = `0x${ethUtil.setLength(ethUtil.toBuffer('0x2'), 20, false).toString('hex')}`;
|
||||
const tokenAddress1 = `0x${ethUtil.setLength(ethUtil.toBuffer('0x1'), 20, false).toString('hex')}`;
|
||||
const tokenAddress2 = `0x${ethUtil.setLength(ethUtil.toBuffer('0x2'), 20, false).toString('hex')}`;
|
||||
|
||||
const token1 = {
|
||||
address: tokenAddress1,
|
||||
name: 'testToken1',
|
||||
symbol: 'TT1',
|
||||
decimals: 18,
|
||||
ipfsHash: `0x${ethUtil.sha3('ipfs1').toString('hex')}`,
|
||||
swarmHash: `0x${ethUtil.sha3('swarm1').toString('hex')}`,
|
||||
};
|
||||
const token1 = {
|
||||
address: tokenAddress1,
|
||||
name: 'testToken1',
|
||||
symbol: 'TT1',
|
||||
decimals: 18,
|
||||
ipfsHash: `0x${ethUtil.sha3('ipfs1').toString('hex')}`,
|
||||
swarmHash: `0x${ethUtil.sha3('swarm1').toString('hex')}`,
|
||||
};
|
||||
|
||||
const token2 = {
|
||||
address: tokenAddress2,
|
||||
name: 'testToken2',
|
||||
symbol: 'TT2',
|
||||
decimals: 18,
|
||||
ipfsHash: `0x${ethUtil.sha3('ipfs2').toString('hex')}`,
|
||||
swarmHash: `0x${ethUtil.sha3('swarm2').toString('hex')}`,
|
||||
};
|
||||
const token2 = {
|
||||
address: tokenAddress2,
|
||||
name: 'testToken2',
|
||||
symbol: 'TT2',
|
||||
decimals: 18,
|
||||
ipfsHash: `0x${ethUtil.sha3('ipfs2').toString('hex')}`,
|
||||
swarmHash: `0x${ethUtil.sha3('swarm2').toString('hex')}`,
|
||||
};
|
||||
|
||||
const nullToken = {
|
||||
address: ZeroEx.NULL_ADDRESS,
|
||||
name: '',
|
||||
symbol: '',
|
||||
decimals: 0,
|
||||
ipfsHash: constants.NULL_BYTES,
|
||||
swarmHash: constants.NULL_BYTES,
|
||||
};
|
||||
const nullToken = {
|
||||
address: ZeroEx.NULL_ADDRESS,
|
||||
name: '',
|
||||
symbol: '',
|
||||
decimals: 0,
|
||||
ipfsHash: constants.NULL_BYTES,
|
||||
swarmHash: constants.NULL_BYTES,
|
||||
};
|
||||
|
||||
let tokenReg: ContractInstance;
|
||||
let tokenRegWrapper: TokenRegWrapper;
|
||||
let tokenReg: ContractInstance;
|
||||
let tokenRegWrapper: TokenRegWrapper;
|
||||
|
||||
beforeEach(async () => {
|
||||
tokenReg = await TokenRegistry.new();
|
||||
tokenRegWrapper = new TokenRegWrapper(tokenReg);
|
||||
});
|
||||
|
||||
describe('addToken', () => {
|
||||
it('should throw when not called by owner', async () => {
|
||||
return expect(tokenRegWrapper.addTokenAsync(token1, notOwner)).to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
|
||||
it('should add token metadata when called by owner', async () => {
|
||||
await tokenRegWrapper.addTokenAsync(token1, owner);
|
||||
const tokenData = await tokenRegWrapper.getTokenMetaDataAsync(token1.address);
|
||||
expect(tokenData).to.be.deep.equal(token1);
|
||||
});
|
||||
|
||||
it('should throw if token already exists', async () => {
|
||||
await tokenRegWrapper.addTokenAsync(token1, owner);
|
||||
|
||||
return expect(tokenRegWrapper.addTokenAsync(token1, owner)).to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
|
||||
it('should throw if token address is null', async () => {
|
||||
return expect(tokenRegWrapper.addTokenAsync(nullToken, owner)).to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
|
||||
it('should throw if name already exists', async () => {
|
||||
await tokenRegWrapper.addTokenAsync(token1, owner);
|
||||
const duplicateNameToken = _.assign({}, token2, {name: token1.name});
|
||||
|
||||
return expect(tokenRegWrapper.addTokenAsync(duplicateNameToken, owner))
|
||||
.to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
|
||||
it('should throw if symbol already exists', async () => {
|
||||
await tokenRegWrapper.addTokenAsync(token1, owner);
|
||||
const duplicateSymbolToken = _.assign({}, token2, {symbol: token1.symbol});
|
||||
|
||||
return expect(tokenRegWrapper.addTokenAsync(duplicateSymbolToken, owner))
|
||||
.to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
});
|
||||
|
||||
describe('after addToken', () => {
|
||||
beforeEach(async () => {
|
||||
await tokenRegWrapper.addTokenAsync(token1, owner);
|
||||
tokenReg = await TokenRegistry.new();
|
||||
tokenRegWrapper = new TokenRegWrapper(tokenReg);
|
||||
});
|
||||
|
||||
describe('getTokenByName', () => {
|
||||
it('should return token metadata when given the token name', async () => {
|
||||
const tokenData = await tokenRegWrapper.getTokenByNameAsync(token1.name);
|
||||
expect(tokenData).to.be.deep.equal(token1);
|
||||
});
|
||||
describe('addToken', () => {
|
||||
it('should throw when not called by owner', async () => {
|
||||
return expect(tokenRegWrapper.addTokenAsync(token1, notOwner)).to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
|
||||
it('should add token metadata when called by owner', async () => {
|
||||
await tokenRegWrapper.addTokenAsync(token1, owner);
|
||||
const tokenData = await tokenRegWrapper.getTokenMetaDataAsync(token1.address);
|
||||
expect(tokenData).to.be.deep.equal(token1);
|
||||
});
|
||||
|
||||
it('should throw if token already exists', async () => {
|
||||
await tokenRegWrapper.addTokenAsync(token1, owner);
|
||||
|
||||
return expect(tokenRegWrapper.addTokenAsync(token1, owner)).to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
|
||||
it('should throw if token address is null', async () => {
|
||||
return expect(tokenRegWrapper.addTokenAsync(nullToken, owner)).to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
|
||||
it('should throw if name already exists', async () => {
|
||||
await tokenRegWrapper.addTokenAsync(token1, owner);
|
||||
const duplicateNameToken = _.assign({}, token2, {name: token1.name});
|
||||
|
||||
return expect(tokenRegWrapper.addTokenAsync(duplicateNameToken, owner))
|
||||
.to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
|
||||
it('should throw if symbol already exists', async () => {
|
||||
await tokenRegWrapper.addTokenAsync(token1, owner);
|
||||
const duplicateSymbolToken = _.assign({}, token2, {symbol: token1.symbol});
|
||||
|
||||
return expect(tokenRegWrapper.addTokenAsync(duplicateSymbolToken, owner))
|
||||
.to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getTokenBySymbol', () => {
|
||||
it('should return token metadata when given the token symbol', async () => {
|
||||
const tokenData = await tokenRegWrapper.getTokenBySymbolAsync(token1.symbol);
|
||||
expect(tokenData).to.be.deep.equal(token1);
|
||||
});
|
||||
describe('after addToken', () => {
|
||||
beforeEach(async () => {
|
||||
await tokenRegWrapper.addTokenAsync(token1, owner);
|
||||
});
|
||||
|
||||
describe('getTokenByName', () => {
|
||||
it('should return token metadata when given the token name', async () => {
|
||||
const tokenData = await tokenRegWrapper.getTokenByNameAsync(token1.name);
|
||||
expect(tokenData).to.be.deep.equal(token1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getTokenBySymbol', () => {
|
||||
it('should return token metadata when given the token symbol', async () => {
|
||||
const tokenData = await tokenRegWrapper.getTokenBySymbolAsync(token1.symbol);
|
||||
expect(tokenData).to.be.deep.equal(token1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('setTokenName', () => {
|
||||
it('should throw when not called by owner', async () => {
|
||||
return expect(tokenReg.setTokenName(token1.address, token2.name, {from: notOwner}))
|
||||
.to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
|
||||
it('should change the token name when called by owner', async () => {
|
||||
const res = await tokenReg.setTokenName(token1.address, token2.name, {from: owner});
|
||||
expect(res.logs).to.have.length(1);
|
||||
const [newData, oldData] = await Promise.all([
|
||||
tokenRegWrapper.getTokenByNameAsync(token2.name),
|
||||
tokenRegWrapper.getTokenByNameAsync(token1.name),
|
||||
]);
|
||||
|
||||
const expectedNewData = _.assign({}, token1, {name: token2.name});
|
||||
const expectedOldData = nullToken;
|
||||
expect(newData).to.be.deep.equal(expectedNewData);
|
||||
expect(oldData).to.be.deep.equal(expectedOldData);
|
||||
});
|
||||
|
||||
it('should throw if the name already exists', async () => {
|
||||
await tokenRegWrapper.addTokenAsync(token2, owner);
|
||||
|
||||
return expect(tokenReg.setTokenName(token1.address, token2.name, {from: owner}))
|
||||
.to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
|
||||
it('should throw if token does not exist', async () => {
|
||||
return expect(tokenReg.setTokenName(nullToken.address, token2.name, {from: owner}))
|
||||
.to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
});
|
||||
|
||||
describe('setTokenSymbol', () => {
|
||||
it('should throw when not called by owner', async () => {
|
||||
return expect(tokenReg.setTokenSymbol(token1.address, token2.symbol, {from: notOwner}))
|
||||
.to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
|
||||
it('should change the token symbol when called by owner', async () => {
|
||||
const res = await tokenReg.setTokenSymbol(token1.address, token2.symbol, {from: owner});
|
||||
expect(res.logs).to.have.length(1);
|
||||
const [newData, oldData] = await Promise.all([
|
||||
tokenRegWrapper.getTokenBySymbolAsync(token2.symbol),
|
||||
tokenRegWrapper.getTokenBySymbolAsync(token1.symbol),
|
||||
]);
|
||||
|
||||
const expectedNewData = _.assign({}, token1, {symbol: token2.symbol});
|
||||
const expectedOldData = nullToken;
|
||||
expect(newData).to.be.deep.equal(expectedNewData);
|
||||
expect(oldData).to.be.deep.equal(expectedOldData);
|
||||
});
|
||||
|
||||
it('should throw if the symbol already exists', async () => {
|
||||
await tokenRegWrapper.addTokenAsync(token2, owner);
|
||||
|
||||
return expect(tokenReg.setTokenSymbol(token1.address, token2.symbol, {from: owner}))
|
||||
.to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
|
||||
it('should throw if token does not exist', async () => {
|
||||
return expect(tokenReg.setTokenSymbol(nullToken.address, token2.symbol, {from: owner}))
|
||||
.to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeToken', () => {
|
||||
it('should throw if not called by owner', async () => {
|
||||
const index = 0;
|
||||
return expect(tokenReg.removeToken(token1.address, index, {from: notOwner}))
|
||||
.to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
|
||||
it('should remove token metadata when called by owner', async () => {
|
||||
const index = 0;
|
||||
const res = await tokenReg.removeToken(token1.address, index, {from: owner});
|
||||
expect(res.logs).to.have.length(1);
|
||||
const tokenData = await tokenRegWrapper.getTokenMetaDataAsync(token1.address);
|
||||
expect(tokenData).to.be.deep.equal(nullToken);
|
||||
});
|
||||
|
||||
it('should throw if token does not exist', async () => {
|
||||
const index = 0;
|
||||
return expect(tokenReg.removeToken(nullToken.address, index, {from: owner}))
|
||||
.to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
|
||||
it('should throw if token at given index does not match address', async () => {
|
||||
await tokenRegWrapper.addTokenAsync(token2, owner);
|
||||
const incorrectIndex = 0;
|
||||
return expect(tokenReg.removeToken(token2.address, incorrectIndex, {from: owner}))
|
||||
.to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('setTokenName', () => {
|
||||
it('should throw when not called by owner', async () => {
|
||||
return expect(tokenReg.setTokenName(token1.address, token2.name, {from: notOwner}))
|
||||
.to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
|
||||
it('should change the token name when called by owner', async () => {
|
||||
const res = await tokenReg.setTokenName(token1.address, token2.name, {from: owner});
|
||||
expect(res.logs).to.have.length(1);
|
||||
const [newData, oldData] = await Promise.all([
|
||||
tokenRegWrapper.getTokenByNameAsync(token2.name),
|
||||
tokenRegWrapper.getTokenByNameAsync(token1.name),
|
||||
]);
|
||||
|
||||
const expectedNewData = _.assign({}, token1, {name: token2.name});
|
||||
const expectedOldData = nullToken;
|
||||
expect(newData).to.be.deep.equal(expectedNewData);
|
||||
expect(oldData).to.be.deep.equal(expectedOldData);
|
||||
});
|
||||
|
||||
it('should throw if the name already exists', async () => {
|
||||
await tokenRegWrapper.addTokenAsync(token2, owner);
|
||||
|
||||
return expect(tokenReg.setTokenName(token1.address, token2.name, {from: owner}))
|
||||
.to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
|
||||
it('should throw if token does not exist', async () => {
|
||||
return expect(tokenReg.setTokenName(nullToken.address, token2.name, {from: owner}))
|
||||
.to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
});
|
||||
|
||||
describe('setTokenSymbol', () => {
|
||||
it('should throw when not called by owner', async () => {
|
||||
return expect(tokenReg.setTokenSymbol(token1.address, token2.symbol, {from: notOwner}))
|
||||
.to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
|
||||
it('should change the token symbol when called by owner', async () => {
|
||||
const res = await tokenReg.setTokenSymbol(token1.address, token2.symbol, {from: owner});
|
||||
expect(res.logs).to.have.length(1);
|
||||
const [newData, oldData] = await Promise.all([
|
||||
tokenRegWrapper.getTokenBySymbolAsync(token2.symbol),
|
||||
tokenRegWrapper.getTokenBySymbolAsync(token1.symbol),
|
||||
]);
|
||||
|
||||
const expectedNewData = _.assign({}, token1, {symbol: token2.symbol});
|
||||
const expectedOldData = nullToken;
|
||||
expect(newData).to.be.deep.equal(expectedNewData);
|
||||
expect(oldData).to.be.deep.equal(expectedOldData);
|
||||
});
|
||||
|
||||
it('should throw if the symbol already exists', async () => {
|
||||
await tokenRegWrapper.addTokenAsync(token2, owner);
|
||||
|
||||
return expect(tokenReg.setTokenSymbol(token1.address, token2.symbol, {from: owner}))
|
||||
.to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
|
||||
it('should throw if token does not exist', async () => {
|
||||
return expect(tokenReg.setTokenSymbol(nullToken.address, token2.symbol, {from: owner}))
|
||||
.to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeToken', () => {
|
||||
it('should throw if not called by owner', async () => {
|
||||
const index = 0;
|
||||
return expect(tokenReg.removeToken(token1.address, index, {from: notOwner}))
|
||||
.to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
|
||||
it('should remove token metadata when called by owner', async () => {
|
||||
const index = 0;
|
||||
const res = await tokenReg.removeToken(token1.address, index, {from: owner});
|
||||
expect(res.logs).to.have.length(1);
|
||||
const tokenData = await tokenRegWrapper.getTokenMetaDataAsync(token1.address);
|
||||
expect(tokenData).to.be.deep.equal(nullToken);
|
||||
});
|
||||
|
||||
it('should throw if token does not exist', async () => {
|
||||
const index = 0;
|
||||
return expect(tokenReg.removeToken(nullToken.address, index, {from: owner}))
|
||||
.to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
|
||||
it('should throw if token at given index does not match address', async () => {
|
||||
await tokenRegWrapper.addTokenAsync(token2, owner);
|
||||
const incorrectIndex = 0;
|
||||
return expect(tokenReg.removeToken(token2.address, incorrectIndex, {from: owner}))
|
||||
.to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,75 +9,75 @@ const expect = chai.expect;
|
||||
const TokenTransferProxy = artifacts.require('./db/TokenTransferProxy.sol');
|
||||
|
||||
contract('TokenTransferProxy', (accounts: string[]) => {
|
||||
const owner = accounts[0];
|
||||
const notOwner = accounts[1];
|
||||
const owner = accounts[0];
|
||||
const notOwner = accounts[1];
|
||||
|
||||
let tokenTransferProxy: ContractInstance;
|
||||
let authorized: string;
|
||||
let notAuthorized = owner;
|
||||
let tokenTransferProxy: ContractInstance;
|
||||
let authorized: string;
|
||||
let notAuthorized = owner;
|
||||
|
||||
before(async () => {
|
||||
tokenTransferProxy = await TokenTransferProxy.deployed();
|
||||
});
|
||||
|
||||
describe('addAuthorizedAddress', () => {
|
||||
it('should throw if not called by owner', async () => {
|
||||
return expect(tokenTransferProxy.addAuthorizedAddress(notOwner, {from: notOwner}))
|
||||
.to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
before(async () => {
|
||||
tokenTransferProxy = await TokenTransferProxy.deployed();
|
||||
});
|
||||
|
||||
it('should allow owner to add an authorized address', async () => {
|
||||
await tokenTransferProxy.addAuthorizedAddress(notAuthorized, {from: owner});
|
||||
authorized = notAuthorized;
|
||||
notAuthorized = null;
|
||||
const isAuthorized = await tokenTransferProxy.authorized.call(authorized);
|
||||
expect(isAuthorized).to.be.true();
|
||||
describe('addAuthorizedAddress', () => {
|
||||
it('should throw if not called by owner', async () => {
|
||||
return expect(tokenTransferProxy.addAuthorizedAddress(notOwner, {from: notOwner}))
|
||||
.to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
|
||||
it('should allow owner to add an authorized address', async () => {
|
||||
await tokenTransferProxy.addAuthorizedAddress(notAuthorized, {from: owner});
|
||||
authorized = notAuthorized;
|
||||
notAuthorized = null;
|
||||
const isAuthorized = await tokenTransferProxy.authorized.call(authorized);
|
||||
expect(isAuthorized).to.be.true();
|
||||
});
|
||||
|
||||
it('should throw if owner attempts to authorize a duplicate address', async () => {
|
||||
return expect(tokenTransferProxy.addAuthorizedAddress(authorized, {from: owner}))
|
||||
.to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw if owner attempts to authorize a duplicate address', async () => {
|
||||
return expect(tokenTransferProxy.addAuthorizedAddress(authorized, {from: owner}))
|
||||
.to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
});
|
||||
describe('removeAuthorizedAddress', () => {
|
||||
it('should throw if not called by owner', async () => {
|
||||
return expect(tokenTransferProxy.removeAuthorizedAddress(authorized, {from: notOwner}))
|
||||
.to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
|
||||
describe('removeAuthorizedAddress', () => {
|
||||
it('should throw if not called by owner', async () => {
|
||||
return expect(tokenTransferProxy.removeAuthorizedAddress(authorized, {from: notOwner}))
|
||||
.to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
it('should allow owner to remove an authorized address', async () => {
|
||||
await tokenTransferProxy.removeAuthorizedAddress(authorized, {from: owner});
|
||||
notAuthorized = authorized;
|
||||
authorized = null;
|
||||
|
||||
const isAuthorized = await tokenTransferProxy.authorized.call(notAuthorized);
|
||||
expect(isAuthorized).to.be.false();
|
||||
});
|
||||
|
||||
it('should throw if owner attempts to remove an address that is not authorized', async () => {
|
||||
return expect(tokenTransferProxy.removeAuthorizedAddress(notAuthorized, {from: owner}))
|
||||
.to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow owner to remove an authorized address', async () => {
|
||||
await tokenTransferProxy.removeAuthorizedAddress(authorized, {from: owner});
|
||||
notAuthorized = authorized;
|
||||
authorized = null;
|
||||
describe('getAuthorizedAddresses', () => {
|
||||
it('should return all authorized addresses', async () => {
|
||||
const initial = await tokenTransferProxy.getAuthorizedAddresses();
|
||||
expect(initial).to.have.length(1);
|
||||
await tokenTransferProxy.addAuthorizedAddress(notAuthorized, {from: owner});
|
||||
|
||||
const isAuthorized = await tokenTransferProxy.authorized.call(notAuthorized);
|
||||
expect(isAuthorized).to.be.false();
|
||||
authorized = notAuthorized;
|
||||
notAuthorized = null;
|
||||
const afterAdd = await tokenTransferProxy.getAuthorizedAddresses();
|
||||
expect(afterAdd).to.have.length(2);
|
||||
expect(afterAdd).to.include(authorized);
|
||||
|
||||
await tokenTransferProxy.removeAuthorizedAddress(authorized, {from: owner});
|
||||
notAuthorized = authorized;
|
||||
authorized = null;
|
||||
const afterRemove = await tokenTransferProxy.getAuthorizedAddresses();
|
||||
expect(afterRemove).to.have.length(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw if owner attempts to remove an address that is not authorized', async () => {
|
||||
return expect(tokenTransferProxy.removeAuthorizedAddress(notAuthorized, {from: owner}))
|
||||
.to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getAuthorizedAddresses', () => {
|
||||
it('should return all authorized addresses', async () => {
|
||||
const initial = await tokenTransferProxy.getAuthorizedAddresses();
|
||||
expect(initial).to.have.length(1);
|
||||
await tokenTransferProxy.addAuthorizedAddress(notAuthorized, {from: owner});
|
||||
|
||||
authorized = notAuthorized;
|
||||
notAuthorized = null;
|
||||
const afterAdd = await tokenTransferProxy.getAuthorizedAddresses();
|
||||
expect(afterAdd).to.have.length(2);
|
||||
expect(afterAdd).to.include(authorized);
|
||||
|
||||
await tokenTransferProxy.removeAuthorizedAddress(authorized, {from: owner});
|
||||
notAuthorized = authorized;
|
||||
authorized = null;
|
||||
const afterRemove = await tokenTransferProxy.getAuthorizedAddresses();
|
||||
expect(afterRemove).to.have.length(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,58 +9,59 @@ import {chaiSetup} from '../utils/chai_setup';
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
const {
|
||||
TokenTransferProxy,
|
||||
DummyToken,
|
||||
TokenRegistry,
|
||||
TokenTransferProxy,
|
||||
DummyToken,
|
||||
TokenRegistry,
|
||||
} = new Artifacts(artifacts);
|
||||
|
||||
contract('TokenTransferProxy', (accounts: string[]) => {
|
||||
const INIT_BAL = 100000000;
|
||||
const INIT_ALLOW = 100000000;
|
||||
const INIT_BAL = 100000000;
|
||||
const INIT_ALLOW = 100000000;
|
||||
|
||||
const owner = accounts[0];
|
||||
const notAuthorized = owner;
|
||||
const owner = accounts[0];
|
||||
const notAuthorized = owner;
|
||||
|
||||
let tokenTransferProxy: ContractInstance;
|
||||
let tokenRegistry: ContractInstance;
|
||||
let rep: ContractInstance;
|
||||
let dmyBalances: Balances;
|
||||
let tokenTransferProxy: ContractInstance;
|
||||
let tokenRegistry: ContractInstance;
|
||||
let rep: ContractInstance;
|
||||
let dmyBalances: Balances;
|
||||
|
||||
before(async () => {
|
||||
[tokenTransferProxy, tokenRegistry] = await Promise.all([
|
||||
TokenTransferProxy.deployed(),
|
||||
TokenRegistry.deployed(),
|
||||
]);
|
||||
const repAddress = await tokenRegistry.getTokenAddressBySymbol('REP');
|
||||
rep = DummyToken.at(repAddress);
|
||||
before(async () => {
|
||||
[tokenTransferProxy, tokenRegistry] = await Promise.all([
|
||||
TokenTransferProxy.deployed(),
|
||||
TokenRegistry.deployed(),
|
||||
]);
|
||||
const repAddress = await tokenRegistry.getTokenAddressBySymbol('REP');
|
||||
rep = DummyToken.at(repAddress);
|
||||
|
||||
dmyBalances = new Balances([rep], [accounts[0], accounts[1]]);
|
||||
await Promise.all([
|
||||
rep.approve(TokenTransferProxy.address, INIT_ALLOW, {from: accounts[0]}),
|
||||
rep.setBalance(accounts[0], INIT_BAL, {from: owner}),
|
||||
rep.approve(TokenTransferProxy.address, INIT_ALLOW, {from: accounts[1]}),
|
||||
rep.setBalance(accounts[1], INIT_BAL, {from: owner}),
|
||||
]);
|
||||
});
|
||||
|
||||
describe('transferFrom', () => {
|
||||
it('should throw when called by an unauthorized address', async () => {
|
||||
expect(tokenTransferProxy.transferFrom(rep.address, accounts[0], accounts[1], 1000, {from: notAuthorized}))
|
||||
.to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
dmyBalances = new Balances([rep], [accounts[0], accounts[1]]);
|
||||
await Promise.all([
|
||||
rep.approve(TokenTransferProxy.address, INIT_ALLOW, {from: accounts[0]}),
|
||||
rep.setBalance(accounts[0], INIT_BAL, {from: owner}),
|
||||
rep.approve(TokenTransferProxy.address, INIT_ALLOW, {from: accounts[1]}),
|
||||
rep.setBalance(accounts[1], INIT_BAL, {from: owner}),
|
||||
]);
|
||||
});
|
||||
|
||||
it('should allow an authorized address to transfer', async () => {
|
||||
const balances = await dmyBalances.getAsync();
|
||||
describe('transferFrom', () => {
|
||||
it('should throw when called by an unauthorized address', async () => {
|
||||
expect(tokenTransferProxy.transferFrom(rep.address, accounts[0], accounts[1], 1000, {from: notAuthorized}))
|
||||
.to.be.rejectedWith(constants.INVALID_OPCODE);
|
||||
});
|
||||
|
||||
await tokenTransferProxy.addAuthorizedAddress(notAuthorized, {from: owner});
|
||||
const transferAmt = 10000;
|
||||
await tokenTransferProxy.transferFrom(rep.address, accounts[0], accounts[1], transferAmt, {from: notAuthorized});
|
||||
it('should allow an authorized address to transfer', async () => {
|
||||
const balances = await dmyBalances.getAsync();
|
||||
|
||||
const newBalances = await dmyBalances.getAsync();
|
||||
expect(newBalances[accounts[0]][rep.address])
|
||||
.to.be.bignumber.equal(balances[accounts[0]][rep.address].minus(transferAmt));
|
||||
expect(newBalances[accounts[1]][rep.address])
|
||||
.to.be.bignumber.equal(balances[accounts[1]][rep.address].add(transferAmt));
|
||||
await tokenTransferProxy.addAuthorizedAddress(notAuthorized, {from: owner});
|
||||
const transferAmt = 10000;
|
||||
await tokenTransferProxy.transferFrom(rep.address, accounts[0], accounts[1],
|
||||
transferAmt, {from: notAuthorized});
|
||||
|
||||
const newBalances = await dmyBalances.getAsync();
|
||||
expect(newBalances[accounts[0]][rep.address])
|
||||
.to.be.bignumber.equal(balances[accounts[0]][rep.address].minus(transferAmt));
|
||||
expect(newBalances[accounts[1]][rep.address])
|
||||
.to.be.bignumber.equal(balances[accounts[1]][rep.address].add(transferAmt));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,102 +14,102 @@ chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
|
||||
contract('UnlimitedAllowanceToken', (accounts: string[]) => {
|
||||
const zeroEx = new ZeroEx(web3.currentProvider);
|
||||
const owner = accounts[0];
|
||||
const spender = accounts[1];
|
||||
const zeroEx = new ZeroEx(web3.currentProvider);
|
||||
const owner = accounts[0];
|
||||
const spender = accounts[1];
|
||||
|
||||
const MAX_MINT_VALUE = new BigNumber(100000000000000000000);
|
||||
let tokenAddress: string;
|
||||
let token: ContractInstance;
|
||||
const MAX_MINT_VALUE = new BigNumber(100000000000000000000);
|
||||
let tokenAddress: string;
|
||||
let token: ContractInstance;
|
||||
|
||||
beforeEach(async () => {
|
||||
token = await DummyToken.new({from: owner});
|
||||
await token.mint(MAX_MINT_VALUE, {from: owner});
|
||||
tokenAddress = token.address;
|
||||
});
|
||||
|
||||
describe('transfer', () => {
|
||||
it('should transfer balance from sender to receiver', async () => {
|
||||
const receiver = spender;
|
||||
const initOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
|
||||
const amountToTransfer = new BigNumber(1);
|
||||
await zeroEx.token.transferAsync(tokenAddress, owner, receiver, amountToTransfer);
|
||||
const finalOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
|
||||
const finalReceiverBalance = await zeroEx.token.getBalanceAsync(tokenAddress, receiver);
|
||||
|
||||
const expectedFinalOwnerBalance = initOwnerBalance.minus(amountToTransfer);
|
||||
const expectedFinalReceiverBalance = amountToTransfer;
|
||||
expect(finalOwnerBalance).to.be.bignumber.equal(expectedFinalOwnerBalance);
|
||||
expect(finalReceiverBalance).to.be.bignumber.equal(expectedFinalReceiverBalance);
|
||||
beforeEach(async () => {
|
||||
token = await DummyToken.new({from: owner});
|
||||
await token.mint(MAX_MINT_VALUE, {from: owner});
|
||||
tokenAddress = token.address;
|
||||
});
|
||||
|
||||
it('should return true on a 0 value transfer', async () => {
|
||||
const didReturnTrue = await token.transfer.call(spender, 0, {from: owner});
|
||||
expect(didReturnTrue).to.be.true();
|
||||
});
|
||||
});
|
||||
describe('transfer', () => {
|
||||
it('should transfer balance from sender to receiver', async () => {
|
||||
const receiver = spender;
|
||||
const initOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
|
||||
const amountToTransfer = new BigNumber(1);
|
||||
await zeroEx.token.transferAsync(tokenAddress, owner, receiver, amountToTransfer);
|
||||
const finalOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
|
||||
const finalReceiverBalance = await zeroEx.token.getBalanceAsync(tokenAddress, receiver);
|
||||
|
||||
describe('transferFrom', () => {
|
||||
it('should return false if owner has insufficient balance', async () => {
|
||||
const ownerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
|
||||
const amountToTransfer = ownerBalance.plus(1);
|
||||
await zeroEx.token.setAllowanceAsync(tokenAddress, owner, spender, amountToTransfer);
|
||||
const didReturnTrue = await token.transferFrom.call(owner, spender, amountToTransfer, {from: spender});
|
||||
expect(didReturnTrue).to.be.false();
|
||||
const expectedFinalOwnerBalance = initOwnerBalance.minus(amountToTransfer);
|
||||
const expectedFinalReceiverBalance = amountToTransfer;
|
||||
expect(finalOwnerBalance).to.be.bignumber.equal(expectedFinalOwnerBalance);
|
||||
expect(finalReceiverBalance).to.be.bignumber.equal(expectedFinalReceiverBalance);
|
||||
});
|
||||
|
||||
it('should return true on a 0 value transfer', async () => {
|
||||
const didReturnTrue = await token.transfer.call(spender, 0, {from: owner});
|
||||
expect(didReturnTrue).to.be.true();
|
||||
});
|
||||
});
|
||||
|
||||
it('should return false if spender has insufficient allowance', async () => {
|
||||
const ownerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
|
||||
const amountToTransfer = ownerBalance;
|
||||
describe('transferFrom', () => {
|
||||
it('should return false if owner has insufficient balance', async () => {
|
||||
const ownerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
|
||||
const amountToTransfer = ownerBalance.plus(1);
|
||||
await zeroEx.token.setAllowanceAsync(tokenAddress, owner, spender, amountToTransfer);
|
||||
const didReturnTrue = await token.transferFrom.call(owner, spender, amountToTransfer, {from: spender});
|
||||
expect(didReturnTrue).to.be.false();
|
||||
});
|
||||
|
||||
const spenderAllowance = await zeroEx.token.getAllowanceAsync(tokenAddress, owner, spender);
|
||||
const spenderAllowanceIsInsufficient = spenderAllowance.cmp(amountToTransfer) < 0;
|
||||
expect(spenderAllowanceIsInsufficient).to.be.true();
|
||||
it('should return false if spender has insufficient allowance', async () => {
|
||||
const ownerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
|
||||
const amountToTransfer = ownerBalance;
|
||||
|
||||
const didReturnTrue = await token.transferFrom.call(owner, spender, amountToTransfer, {from: spender});
|
||||
expect(didReturnTrue).to.be.false();
|
||||
const spenderAllowance = await zeroEx.token.getAllowanceAsync(tokenAddress, owner, spender);
|
||||
const spenderAllowanceIsInsufficient = spenderAllowance.cmp(amountToTransfer) < 0;
|
||||
expect(spenderAllowanceIsInsufficient).to.be.true();
|
||||
|
||||
const didReturnTrue = await token.transferFrom.call(owner, spender, amountToTransfer, {from: spender});
|
||||
expect(didReturnTrue).to.be.false();
|
||||
});
|
||||
|
||||
it('should return true on a 0 value transfer', async () => {
|
||||
const amountToTransfer = 0;
|
||||
const didReturnTrue = await token.transferFrom.call(owner, spender, amountToTransfer, {from: spender});
|
||||
expect(didReturnTrue).to.be.true();
|
||||
});
|
||||
|
||||
it('should not modify spender allowance if spender allowance is 2^256 - 1', async () => {
|
||||
const initOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
|
||||
const amountToTransfer = initOwnerBalance;
|
||||
const initSpenderAllowance = zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS;
|
||||
await zeroEx.token.setAllowanceAsync(tokenAddress, owner, spender, initSpenderAllowance);
|
||||
await zeroEx.token.transferFromAsync(tokenAddress, owner, spender, spender, amountToTransfer);
|
||||
|
||||
const newSpenderAllowance = await zeroEx.token.getAllowanceAsync(tokenAddress, owner, spender);
|
||||
expect(initSpenderAllowance).to.be.bignumber.equal(newSpenderAllowance);
|
||||
});
|
||||
|
||||
it('should transfer the correct balances if spender has sufficient allowance', async () => {
|
||||
const initOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
|
||||
const amountToTransfer = initOwnerBalance;
|
||||
const initSpenderAllowance = initOwnerBalance;
|
||||
await zeroEx.token.setAllowanceAsync(tokenAddress, owner, spender, initSpenderAllowance);
|
||||
await zeroEx.token.transferFromAsync(tokenAddress, owner, spender, spender, amountToTransfer);
|
||||
|
||||
const newOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
|
||||
const newSpenderBalance = await zeroEx.token.getBalanceAsync(tokenAddress, spender);
|
||||
|
||||
expect(newOwnerBalance).to.be.bignumber.equal(0);
|
||||
expect(newSpenderBalance).to.be.bignumber.equal(initOwnerBalance);
|
||||
});
|
||||
|
||||
it('should modify allowance if spender has sufficient allowance less than 2^256 - 1', async () => {
|
||||
const initOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
|
||||
const amountToTransfer = initOwnerBalance;
|
||||
const initSpenderAllowance = initOwnerBalance;
|
||||
await zeroEx.token.setAllowanceAsync(tokenAddress, owner, spender, initSpenderAllowance);
|
||||
await zeroEx.token.transferFromAsync(tokenAddress, owner, spender, spender, amountToTransfer);
|
||||
|
||||
const newSpenderAllowance = await zeroEx.token.getAllowanceAsync(tokenAddress, owner, spender);
|
||||
expect(newSpenderAllowance).to.be.bignumber.equal(0);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return true on a 0 value transfer', async () => {
|
||||
const amountToTransfer = 0;
|
||||
const didReturnTrue = await token.transferFrom.call(owner, spender, amountToTransfer, {from: spender});
|
||||
expect(didReturnTrue).to.be.true();
|
||||
});
|
||||
|
||||
it('should not modify spender allowance if spender allowance is 2^256 - 1', async () => {
|
||||
const initOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
|
||||
const amountToTransfer = initOwnerBalance;
|
||||
const initSpenderAllowance = zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS;
|
||||
await zeroEx.token.setAllowanceAsync(tokenAddress, owner, spender, initSpenderAllowance);
|
||||
await zeroEx.token.transferFromAsync(tokenAddress, owner, spender, spender, amountToTransfer);
|
||||
|
||||
const newSpenderAllowance = await zeroEx.token.getAllowanceAsync(tokenAddress, owner, spender);
|
||||
expect(initSpenderAllowance).to.be.bignumber.equal(newSpenderAllowance);
|
||||
});
|
||||
|
||||
it('should transfer the correct balances if spender has sufficient allowance', async () => {
|
||||
const initOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
|
||||
const amountToTransfer = initOwnerBalance;
|
||||
const initSpenderAllowance = initOwnerBalance;
|
||||
await zeroEx.token.setAllowanceAsync(tokenAddress, owner, spender, initSpenderAllowance);
|
||||
await zeroEx.token.transferFromAsync(tokenAddress, owner, spender, spender, amountToTransfer);
|
||||
|
||||
const newOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
|
||||
const newSpenderBalance = await zeroEx.token.getBalanceAsync(tokenAddress, spender);
|
||||
|
||||
expect(newOwnerBalance).to.be.bignumber.equal(0);
|
||||
expect(newSpenderBalance).to.be.bignumber.equal(initOwnerBalance);
|
||||
});
|
||||
|
||||
it('should modify allowance if spender has sufficient allowance less than 2^256 - 1', async () => {
|
||||
const initOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner);
|
||||
const amountToTransfer = initOwnerBalance;
|
||||
const initSpenderAllowance = initOwnerBalance;
|
||||
await zeroEx.token.setAllowanceAsync(tokenAddress, owner, spender, initSpenderAllowance);
|
||||
await zeroEx.token.transferFromAsync(tokenAddress, owner, spender, spender, amountToTransfer);
|
||||
|
||||
const newSpenderAllowance = await zeroEx.token.getAllowanceAsync(tokenAddress, owner, spender);
|
||||
expect(newSpenderAllowance).to.be.bignumber.equal(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,155 +14,155 @@ const {Exchange, ZRXToken} = new Artifacts(artifacts);
|
||||
const web3: Web3 = (global as any).web3;
|
||||
|
||||
contract('ZRXToken', (accounts: string[]) => {
|
||||
const owner = accounts[0];
|
||||
const spender = accounts[1];
|
||||
let zeroEx: ZeroEx;
|
||||
const owner = accounts[0];
|
||||
const spender = accounts[1];
|
||||
let zeroEx: ZeroEx;
|
||||
|
||||
let MAX_UINT: BigNumber;
|
||||
let MAX_UINT: BigNumber;
|
||||
|
||||
let zrx: ContractInstance;
|
||||
let zrxAddress: string;
|
||||
let zrx: ContractInstance;
|
||||
let zrxAddress: string;
|
||||
|
||||
beforeEach(async () => {
|
||||
zeroEx = new ZeroEx(web3.currentProvider, {
|
||||
exchangeContractAddress: Exchange.address,
|
||||
});
|
||||
zrxAddress = await zeroEx.exchange.getZRXTokenAddressAsync();
|
||||
zrx = await ZRXToken.at(zrxAddress);
|
||||
MAX_UINT = zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS;
|
||||
});
|
||||
|
||||
describe('constants', () => {
|
||||
it('should have 18 decimals', async () => {
|
||||
const decimals = new BigNumber(await zrx.decimals.call());
|
||||
const expectedDecimals = 18;
|
||||
expect(decimals).to.be.bignumber.equal(expectedDecimals);
|
||||
beforeEach(async () => {
|
||||
zeroEx = new ZeroEx(web3.currentProvider, {
|
||||
exchangeContractAddress: Exchange.address,
|
||||
});
|
||||
zrxAddress = await zeroEx.exchange.getZRXTokenAddressAsync();
|
||||
zrx = await ZRXToken.at(zrxAddress);
|
||||
MAX_UINT = zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS;
|
||||
});
|
||||
|
||||
it('should have a total supply of 1 billion tokens', async () => {
|
||||
const totalSupply = new BigNumber(await zrx.totalSupply.call());
|
||||
const expectedTotalSupply = 1000000000;
|
||||
expect(ZeroEx.toUnitAmount(totalSupply, 18)).to.be.bignumber.equal(expectedTotalSupply);
|
||||
describe('constants', () => {
|
||||
it('should have 18 decimals', async () => {
|
||||
const decimals = new BigNumber(await zrx.decimals.call());
|
||||
const expectedDecimals = 18;
|
||||
expect(decimals).to.be.bignumber.equal(expectedDecimals);
|
||||
});
|
||||
|
||||
it('should have a total supply of 1 billion tokens', async () => {
|
||||
const totalSupply = new BigNumber(await zrx.totalSupply.call());
|
||||
const expectedTotalSupply = 1000000000;
|
||||
expect(ZeroEx.toUnitAmount(totalSupply, 18)).to.be.bignumber.equal(expectedTotalSupply);
|
||||
});
|
||||
|
||||
it('should be named 0x Protocol Token', async () => {
|
||||
const name = await zrx.name.call();
|
||||
const expectedName = '0x Protocol Token';
|
||||
expect(name).to.be.equal(expectedName);
|
||||
});
|
||||
|
||||
it('should have the symbol ZRX', async () => {
|
||||
const symbol = await zrx.symbol.call();
|
||||
const expectedSymbol = 'ZRX';
|
||||
expect(symbol).to.be.equal(expectedSymbol);
|
||||
});
|
||||
});
|
||||
|
||||
it('should be named 0x Protocol Token', async () => {
|
||||
const name = await zrx.name.call();
|
||||
const expectedName = '0x Protocol Token';
|
||||
expect(name).to.be.equal(expectedName);
|
||||
describe('constructor', () => {
|
||||
it('should initialize owner balance to totalSupply', async () => {
|
||||
const ownerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner);
|
||||
const totalSupply = new BigNumber(await zrx.totalSupply.call());
|
||||
expect(totalSupply).to.be.bignumber.equal(ownerBalance);
|
||||
});
|
||||
});
|
||||
|
||||
it('should have the symbol ZRX', async () => {
|
||||
const symbol = await zrx.symbol.call();
|
||||
const expectedSymbol = 'ZRX';
|
||||
expect(symbol).to.be.equal(expectedSymbol);
|
||||
});
|
||||
});
|
||||
describe('transfer', () => {
|
||||
it('should transfer balance from sender to receiver', async () => {
|
||||
const receiver = spender;
|
||||
const initOwnerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner);
|
||||
const amountToTransfer = new BigNumber(1);
|
||||
const txHash = await zeroEx.token.transferAsync(zrxAddress, owner, receiver, amountToTransfer);
|
||||
await zeroEx.awaitTransactionMinedAsync(txHash);
|
||||
const finalOwnerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner);
|
||||
const finalReceiverBalance = await zeroEx.token.getBalanceAsync(zrxAddress, receiver);
|
||||
|
||||
describe('constructor', () => {
|
||||
it('should initialize owner balance to totalSupply', async () => {
|
||||
const ownerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner);
|
||||
const totalSupply = new BigNumber(await zrx.totalSupply.call());
|
||||
expect(totalSupply).to.be.bignumber.equal(ownerBalance);
|
||||
});
|
||||
});
|
||||
const expectedFinalOwnerBalance = initOwnerBalance.minus(amountToTransfer);
|
||||
const expectedFinalReceiverBalance = amountToTransfer;
|
||||
expect(finalOwnerBalance).to.be.bignumber.equal(expectedFinalOwnerBalance);
|
||||
expect(finalReceiverBalance).to.be.bignumber.equal(expectedFinalReceiverBalance);
|
||||
});
|
||||
|
||||
describe('transfer', () => {
|
||||
it('should transfer balance from sender to receiver', async () => {
|
||||
const receiver = spender;
|
||||
const initOwnerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner);
|
||||
const amountToTransfer = new BigNumber(1);
|
||||
const txHash = await zeroEx.token.transferAsync(zrxAddress, owner, receiver, amountToTransfer);
|
||||
await zeroEx.awaitTransactionMinedAsync(txHash);
|
||||
const finalOwnerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner);
|
||||
const finalReceiverBalance = await zeroEx.token.getBalanceAsync(zrxAddress, receiver);
|
||||
|
||||
const expectedFinalOwnerBalance = initOwnerBalance.minus(amountToTransfer);
|
||||
const expectedFinalReceiverBalance = amountToTransfer;
|
||||
expect(finalOwnerBalance).to.be.bignumber.equal(expectedFinalOwnerBalance);
|
||||
expect(finalReceiverBalance).to.be.bignumber.equal(expectedFinalReceiverBalance);
|
||||
it('should return true on a 0 value transfer', async () => {
|
||||
const didReturnTrue = await zrx.transfer.call(spender, 0, {from: owner});
|
||||
expect(didReturnTrue).to.be.true();
|
||||
});
|
||||
});
|
||||
|
||||
it('should return true on a 0 value transfer', async () => {
|
||||
const didReturnTrue = await zrx.transfer.call(spender, 0, {from: owner});
|
||||
expect(didReturnTrue).to.be.true();
|
||||
describe('transferFrom', () => {
|
||||
it('should return false if owner has insufficient balance', async () => {
|
||||
const ownerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner);
|
||||
const amountToTransfer = ownerBalance.plus(1);
|
||||
let txHash = await zeroEx.token.setAllowanceAsync(zrxAddress, owner, spender, amountToTransfer);
|
||||
await zeroEx.awaitTransactionMinedAsync(txHash);
|
||||
const didReturnTrue = await zrx.transferFrom.call(owner, spender, amountToTransfer, {from: spender});
|
||||
expect(didReturnTrue).to.be.false();
|
||||
// Reset allowance
|
||||
txHash = await zeroEx.token.setAllowanceAsync(zrxAddress, owner, spender, new BigNumber(0));
|
||||
await zeroEx.awaitTransactionMinedAsync(txHash);
|
||||
});
|
||||
|
||||
it('should return false if spender has insufficient allowance', async () => {
|
||||
const ownerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner);
|
||||
const amountToTransfer = ownerBalance;
|
||||
|
||||
const spenderAllowance = await zeroEx.token.getAllowanceAsync(zrxAddress, owner, spender);
|
||||
const spenderAllowanceIsInsufficient = spenderAllowance.cmp(amountToTransfer) < 0;
|
||||
expect(spenderAllowanceIsInsufficient).to.be.true();
|
||||
|
||||
const didReturnTrue = await zrx.transferFrom.call(owner, spender, amountToTransfer, {from: spender});
|
||||
expect(didReturnTrue).to.be.false();
|
||||
});
|
||||
|
||||
it('should return true on a 0 value transfer', async () => {
|
||||
const amountToTransfer = 0;
|
||||
const didReturnTrue = await zrx.transferFrom.call(owner, spender, amountToTransfer, {from: spender});
|
||||
expect(didReturnTrue).to.be.true();
|
||||
});
|
||||
|
||||
it('should not modify spender allowance if spender allowance is 2^256 - 1', async () => {
|
||||
const initOwnerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner);
|
||||
const amountToTransfer = initOwnerBalance;
|
||||
const initSpenderAllowance = MAX_UINT;
|
||||
let txHash = await zeroEx.token.setAllowanceAsync(zrxAddress, owner, spender, initSpenderAllowance);
|
||||
await zeroEx.awaitTransactionMinedAsync(txHash);
|
||||
txHash = await zeroEx.token.transferFromAsync(zrxAddress, owner, spender, spender, amountToTransfer);
|
||||
await zeroEx.awaitTransactionMinedAsync(txHash);
|
||||
|
||||
const newSpenderAllowance = await zeroEx.token.getAllowanceAsync(zrxAddress, owner, spender);
|
||||
expect(initSpenderAllowance).to.be.bignumber.equal(newSpenderAllowance);
|
||||
// Restore balance
|
||||
txHash = await zeroEx.token.transferAsync(zrxAddress, spender, owner, amountToTransfer);
|
||||
await zeroEx.awaitTransactionMinedAsync(txHash);
|
||||
});
|
||||
|
||||
it('should transfer the correct balances if spender has sufficient allowance', async () => {
|
||||
const initOwnerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner);
|
||||
const initSpenderBalance = await zeroEx.token.getBalanceAsync(zrxAddress, spender);
|
||||
const amountToTransfer = initOwnerBalance;
|
||||
const initSpenderAllowance = initOwnerBalance;
|
||||
let txHash = await zeroEx.token.setAllowanceAsync(zrxAddress, owner, spender, initSpenderAllowance);
|
||||
await zeroEx.awaitTransactionMinedAsync(txHash);
|
||||
txHash = await zeroEx.token.transferFromAsync(zrxAddress, owner, spender, spender, amountToTransfer);
|
||||
await zeroEx.awaitTransactionMinedAsync(txHash);
|
||||
|
||||
const newOwnerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner);
|
||||
const newSpenderBalance = await zeroEx.token.getBalanceAsync(zrxAddress, spender);
|
||||
|
||||
expect(newOwnerBalance).to.be.bignumber.equal(0);
|
||||
expect(newSpenderBalance).to.be.bignumber.equal(initSpenderBalance.plus(initOwnerBalance));
|
||||
});
|
||||
|
||||
it('should modify allowance if spender has sufficient allowance less than 2^256 - 1', async () => {
|
||||
const initOwnerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner);
|
||||
const amountToTransfer = initOwnerBalance;
|
||||
const initSpenderAllowance = initOwnerBalance;
|
||||
let txHash = await zeroEx.token.setAllowanceAsync(zrxAddress, owner, spender, initSpenderAllowance);
|
||||
await zeroEx.awaitTransactionMinedAsync(txHash);
|
||||
txHash = await zeroEx.token.transferFromAsync(zrxAddress, owner, spender, spender, amountToTransfer);
|
||||
await zeroEx.awaitTransactionMinedAsync(txHash);
|
||||
|
||||
const newSpenderAllowance = await zeroEx.token.getAllowanceAsync(zrxAddress, owner, spender);
|
||||
expect(newSpenderAllowance).to.be.bignumber.equal(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('transferFrom', () => {
|
||||
it('should return false if owner has insufficient balance', async () => {
|
||||
const ownerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner);
|
||||
const amountToTransfer = ownerBalance.plus(1);
|
||||
let txHash = await zeroEx.token.setAllowanceAsync(zrxAddress, owner, spender, amountToTransfer);
|
||||
await zeroEx.awaitTransactionMinedAsync(txHash);
|
||||
const didReturnTrue = await zrx.transferFrom.call(owner, spender, amountToTransfer, {from: spender});
|
||||
expect(didReturnTrue).to.be.false();
|
||||
// Reset allowance
|
||||
txHash = await zeroEx.token.setAllowanceAsync(zrxAddress, owner, spender, new BigNumber(0));
|
||||
await zeroEx.awaitTransactionMinedAsync(txHash);
|
||||
});
|
||||
|
||||
it('should return false if spender has insufficient allowance', async () => {
|
||||
const ownerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner);
|
||||
const amountToTransfer = ownerBalance;
|
||||
|
||||
const spenderAllowance = await zeroEx.token.getAllowanceAsync(zrxAddress, owner, spender);
|
||||
const spenderAllowanceIsInsufficient = spenderAllowance.cmp(amountToTransfer) < 0;
|
||||
expect(spenderAllowanceIsInsufficient).to.be.true();
|
||||
|
||||
const didReturnTrue = await zrx.transferFrom.call(owner, spender, amountToTransfer, {from: spender});
|
||||
expect(didReturnTrue).to.be.false();
|
||||
});
|
||||
|
||||
it('should return true on a 0 value transfer', async () => {
|
||||
const amountToTransfer = 0;
|
||||
const didReturnTrue = await zrx.transferFrom.call(owner, spender, amountToTransfer, {from: spender});
|
||||
expect(didReturnTrue).to.be.true();
|
||||
});
|
||||
|
||||
it('should not modify spender allowance if spender allowance is 2^256 - 1', async () => {
|
||||
const initOwnerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner);
|
||||
const amountToTransfer = initOwnerBalance;
|
||||
const initSpenderAllowance = MAX_UINT;
|
||||
let txHash = await zeroEx.token.setAllowanceAsync(zrxAddress, owner, spender, initSpenderAllowance);
|
||||
await zeroEx.awaitTransactionMinedAsync(txHash);
|
||||
txHash = await zeroEx.token.transferFromAsync(zrxAddress, owner, spender, spender, amountToTransfer);
|
||||
await zeroEx.awaitTransactionMinedAsync(txHash);
|
||||
|
||||
const newSpenderAllowance = await zeroEx.token.getAllowanceAsync(zrxAddress, owner, spender);
|
||||
expect(initSpenderAllowance).to.be.bignumber.equal(newSpenderAllowance);
|
||||
// Restore balance
|
||||
txHash = await zeroEx.token.transferAsync(zrxAddress, spender, owner, amountToTransfer);
|
||||
await zeroEx.awaitTransactionMinedAsync(txHash);
|
||||
});
|
||||
|
||||
it('should transfer the correct balances if spender has sufficient allowance', async () => {
|
||||
const initOwnerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner);
|
||||
const initSpenderBalance = await zeroEx.token.getBalanceAsync(zrxAddress, spender);
|
||||
const amountToTransfer = initOwnerBalance;
|
||||
const initSpenderAllowance = initOwnerBalance;
|
||||
let txHash = await zeroEx.token.setAllowanceAsync(zrxAddress, owner, spender, initSpenderAllowance);
|
||||
await zeroEx.awaitTransactionMinedAsync(txHash);
|
||||
txHash = await zeroEx.token.transferFromAsync(zrxAddress, owner, spender, spender, amountToTransfer);
|
||||
await zeroEx.awaitTransactionMinedAsync(txHash);
|
||||
|
||||
const newOwnerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner);
|
||||
const newSpenderBalance = await zeroEx.token.getBalanceAsync(zrxAddress, spender);
|
||||
|
||||
expect(newOwnerBalance).to.be.bignumber.equal(0);
|
||||
expect(newSpenderBalance).to.be.bignumber.equal(initSpenderBalance.plus(initOwnerBalance));
|
||||
});
|
||||
|
||||
it('should modify allowance if spender has sufficient allowance less than 2^256 - 1', async () => {
|
||||
const initOwnerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner);
|
||||
const amountToTransfer = initOwnerBalance;
|
||||
const initSpenderAllowance = initOwnerBalance;
|
||||
let txHash = await zeroEx.token.setAllowanceAsync(zrxAddress, owner, spender, initSpenderAllowance);
|
||||
await zeroEx.awaitTransactionMinedAsync(txHash);
|
||||
txHash = await zeroEx.token.transferFromAsync(zrxAddress, owner, spender, spender, amountToTransfer);
|
||||
await zeroEx.awaitTransactionMinedAsync(txHash);
|
||||
|
||||
const newSpenderAllowance = await zeroEx.token.getAllowanceAsync(zrxAddress, owner, spender);
|
||||
expect(newSpenderAllowance).to.be.bignumber.equal(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user