From 760c8abc99f6e652bf4d58e62e4e35b3f742558e Mon Sep 17 00:00:00 2001 From: Victor Wiebe Date: Tue, 17 Dec 2019 12:11:22 -0800 Subject: [PATCH 01/33] refactor: resolver in its own function --- src/procedures/FinalizeSto.ts | 59 +++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/src/procedures/FinalizeSto.ts b/src/procedures/FinalizeSto.ts index 8b8b1c2..110c596 100644 --- a/src/procedures/FinalizeSto.ts +++ b/src/procedures/FinalizeSto.ts @@ -10,6 +10,37 @@ import { import { PolymathError } from '../PolymathError'; import { isValidAddress } from '../utils'; import { SecurityToken, SimpleSto, TieredSto } from '../entities'; +import { Factories } from '../Context'; + +export const createRefreshSimpleStoFactoryResolver = ( + factories: Factories, + symbol: string, + stoType: StoType, + stoAddress: string +) => async () => { + const securityTokenId = SecurityToken.generateId({ symbol }); + + switch (stoType) { + case StoType.Simple: { + return factories.simpleStoFactory.refresh( + SimpleSto.generateId({ + securityTokenId, + stoType, + address: stoAddress, + }) + ); + } + case StoType.Tiered: { + return factories.tieredStoFactory.refresh( + TieredSto.generateId({ + securityTokenId, + stoType, + address: stoAddress, + }) + ); + } + } +}; export class FinalizeSto extends Procedure { public type = ProcedureType.FinalizeSto; @@ -118,35 +149,9 @@ export class FinalizeSto extends Procedure { /** * Transactions */ - await this.addTransaction(stoModule.finalize, { tag: PolyTransactionTag.FinalizeSto, - resolvers: [ - async () => { - const securityTokenId = SecurityToken.generateId({ symbol }); - - switch (stoType) { - case StoType.Simple: { - return factories.simpleStoFactory.refresh( - SimpleSto.generateId({ - securityTokenId, - stoType, - address: stoAddress, - }) - ); - } - case StoType.Tiered: { - return factories.tieredStoFactory.refresh( - TieredSto.generateId({ - securityTokenId, - stoType, - address: stoAddress, - }) - ); - } - } - }, - ], + resolvers: [createRefreshSimpleStoFactoryResolver(factories, symbol, stoType, stoAddress)], })({}); } } From 80edb84da9870e02b45a785f22b9b84f422da951 Mon Sep 17 00:00:00 2001 From: Victor Wiebe Date: Tue, 17 Dec 2019 12:24:55 -0800 Subject: [PATCH 02/33] fix: remove redundant code from toggle pause sto --- src/procedures/__tests__/TogglePauseSto.ts | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/procedures/__tests__/TogglePauseSto.ts b/src/procedures/__tests__/TogglePauseSto.ts index 99110d1..d22fa77 100644 --- a/src/procedures/__tests__/TogglePauseSto.ts +++ b/src/procedures/__tests__/TogglePauseSto.ts @@ -16,7 +16,6 @@ import * as simpleStoFactoryModule from '../../entities/factories/SimpleStoFacto import * as tieredStoFactoryModule from '../../entities/factories/TieredStoFactory'; import * as contextModule from '../../Context'; import * as wrappersModule from '../../PolymathBase'; -import * as tokenFactoryModule from '../../testUtils/MockedTokenFactoryModule'; import * as moduleWrapperFactoryModule from '../../testUtils/MockedModuleWrapperFactoryModule'; import { mockFactories } from '../../testUtils/mockFactories'; import { Factories } from '../../Context'; @@ -42,7 +41,6 @@ describe('PauseSto', () => { let target: TogglePauseSto; let contextMock: MockManager; let wrappersMock: MockManager; - let tokenFactoryMock: MockManager; let moduleWrapperFactoryMock: MockManager< moduleWrapperFactoryModule.MockedModuleWrapperFactoryModule >; @@ -54,8 +52,6 @@ describe('PauseSto', () => { let tieredStoFactoryMock: MockManager; - let securityTokenMock: MockManager; - let factoryMockSetup: Factories; let securityTokenId: string; @@ -63,23 +59,14 @@ describe('PauseSto', () => { // Mock the context, wrappers, and tokenFactory to test PauseSto contextMock = ImportMock.mockClass(contextModule, 'Context'); wrappersMock = ImportMock.mockClass(wrappersModule, 'PolymathBase'); - tokenFactoryMock = ImportMock.mockClass(tokenFactoryModule, 'MockedTokenFactoryModule'); moduleWrapperFactoryMock = ImportMock.mockClass( moduleWrapperFactoryModule, 'MockedModuleWrapperFactoryModule' ); contextMock.set('contractWrappers', wrappersMock.getMockInstance()); - wrappersMock.set('tokenFactory', tokenFactoryMock.getMockInstance()); wrappersMock.set('moduleFactory', moduleWrapperFactoryMock.getMockInstance()); - securityTokenMock = ImportMock.mockClass(contractWrappersModule, 'SecurityToken_3_0_0'); - - tokenFactoryMock.mock( - 'getSecurityTokenInstanceFromTicker', - securityTokenMock.getMockInstance() - ); - simpleStoFactoryMock = ImportMock.mockClass(simpleStoFactoryModule, 'SimpleStoFactory'); tieredStoFactoryMock = ImportMock.mockClass(tieredStoFactoryModule, 'TieredStoFactory'); From 33025adbef9738f23d5666de5e9e8b9395664f35 Mon Sep 17 00:00:00 2001 From: Victor Wiebe Date: Wed, 18 Dec 2019 13:12:17 -0800 Subject: [PATCH 03/33] test: finalizeSto and make changes to do so --- src/procedures/FinalizeSto.ts | 27 +- src/procedures/__tests__/FinalizeSto.ts | 318 +++++++++++++++++++++ src/procedures/__tests__/TogglePauseSto.ts | 2 +- 3 files changed, 337 insertions(+), 10 deletions(-) create mode 100644 src/procedures/__tests__/FinalizeSto.ts diff --git a/src/procedures/FinalizeSto.ts b/src/procedures/FinalizeSto.ts index 110c596..3f435f4 100644 --- a/src/procedures/FinalizeSto.ts +++ b/src/procedures/FinalizeSto.ts @@ -12,7 +12,7 @@ import { isValidAddress } from '../utils'; import { SecurityToken, SimpleSto, TieredSto } from '../entities'; import { Factories } from '../Context'; -export const createRefreshSimpleStoFactoryResolver = ( +export const createRefreshStoFactoryResolver = ( factories: Factories, symbol: string, stoType: StoType, @@ -73,6 +73,13 @@ export class FinalizeSto extends Procedure { }); } + function throwStoModuleError() { + throw new PolymathError({ + code: ErrorCode.ProcedureValidationError, + message: `STO ${stoAddress} is either archived or hasn't been launched`, + }); + } + let stoModule; let remainingTokens: BigNumber; @@ -83,6 +90,10 @@ export class FinalizeSto extends Procedure { address: stoAddress, }); + if (!stoModule) { + throwStoModuleError(); + } + if (isCappedSTO_3_0_0(stoModule)) { throw new PolymathError({ code: ErrorCode.IncorrectVersion, @@ -90,6 +101,7 @@ export class FinalizeSto extends Procedure { 'Capped STO version is 3.0.0. Version 3.1.0 or greater is required for forced finalization', }); } + const { totalTokensSold, cap } = await stoModule.getSTODetails(); remainingTokens = cap.minus(totalTokensSold); @@ -101,6 +113,10 @@ export class FinalizeSto extends Procedure { address: stoAddress, }); + if (!stoModule) { + throwStoModuleError(); + } + const { tokensSold, capPerTier } = await stoModule.getSTODetails(); const totalCap = capPerTier.reduce((prev, next) => prev.plus(next), new BigNumber(0)); remainingTokens = totalCap.minus(tokensSold); @@ -115,13 +131,6 @@ export class FinalizeSto extends Procedure { } } - if (!stoModule) { - throw new PolymathError({ - code: ErrorCode.ProcedureValidationError, - message: `STO ${stoAddress} is either archived or hasn't been launched`, - }); - } - const [isFinalized, treasuryWallet] = await Promise.all([ stoModule.isFinalized(), contractWrappers.getTreasuryWallet({ module: stoModule }), @@ -151,7 +160,7 @@ export class FinalizeSto extends Procedure { */ await this.addTransaction(stoModule.finalize, { tag: PolyTransactionTag.FinalizeSto, - resolvers: [createRefreshSimpleStoFactoryResolver(factories, symbol, stoType, stoAddress)], + resolvers: [createRefreshStoFactoryResolver(factories, symbol, stoType, stoAddress)], })({}); } } diff --git a/src/procedures/__tests__/FinalizeSto.ts b/src/procedures/__tests__/FinalizeSto.ts new file mode 100644 index 0000000..4195a74 --- /dev/null +++ b/src/procedures/__tests__/FinalizeSto.ts @@ -0,0 +1,318 @@ +import { ImportMock, MockManager } from 'ts-mock-imports'; +import { restore, spy } from 'sinon'; +import * as contractWrappersModule from '@polymathnetwork/contract-wrappers'; +import { BigNumber } from '@polymathnetwork/contract-wrappers'; +import { TransferStatusCode } from '@polymathnetwork/contract-wrappers'; +import { FinalizeSto } from '../FinalizeSto'; +import { Procedure } from '../Procedure'; +import { PolymathError } from '../../PolymathError'; +import { + ErrorCode, + FinalizeStoProcedureArgs, + PolyTransactionTag, + ProcedureType, + StoType, +} from '../../types'; +import * as finalizeStoModule from '../FinalizeSto'; +import * as simpleStoFactoryModule from '../../entities/factories/SimpleStoFactory'; +import * as tieredStoFactoryModule from '../../entities/factories/TieredStoFactory'; +import * as contextModule from '../../Context'; +import * as wrappersModule from '../../PolymathBase'; +import * as tokenFactoryModule from '../../testUtils/MockedTokenFactoryModule'; +import * as moduleWrapperFactoryModule from '../../testUtils/MockedModuleWrapperFactoryModule'; +import { mockFactories } from '../../testUtils/mockFactories'; +import { Factories } from '../../Context'; +import { SimpleSto, SecurityToken, TieredSto } from '../../entities'; + +const simpleParams: FinalizeStoProcedureArgs = { + symbol: 'TEST1', + stoAddress: '0x5555555555555555555555555555555555555555', + stoType: StoType.Simple, +}; + +const tieredParams: FinalizeStoProcedureArgs = { + symbol: 'TEST1', + stoAddress: '0x6666666666666666666666666666666666666666', + stoType: StoType.Tiered, +}; + +const invalidSto = 'InvalidSto'; +const treasuryWallet = '0x1111111111111111111111111111111111111111'; +const amountOfTokens = new BigNumber(1); + +describe('FinalizeSto', () => { + let target: FinalizeSto; + let contextMock: MockManager; + let wrappersMock: MockManager; + let tokenFactoryMock: MockManager; + let moduleWrapperFactoryMock: MockManager< + moduleWrapperFactoryModule.MockedModuleWrapperFactoryModule + >; + let simpleSto_3_0_0_Mock: MockManager; + let simpleSto_3_1_0_Mock: MockManager; + let tieredStoMock: MockManager; + + // Mock factories + let simpleStoFactoryMock: MockManager; + + let tieredStoFactoryMock: MockManager; + + let securityTokenMock: MockManager; + + let factoryMockSetup: Factories; + let securityTokenId: string; + + beforeEach(() => { + // Mock the context, wrappers, and tokenFactory to test FinalizeSto + contextMock = ImportMock.mockClass(contextModule, 'Context'); + wrappersMock = ImportMock.mockClass(wrappersModule, 'PolymathBase'); + tokenFactoryMock = ImportMock.mockClass(tokenFactoryModule, 'MockedTokenFactoryModule'); + moduleWrapperFactoryMock = ImportMock.mockClass( + moduleWrapperFactoryModule, + 'MockedModuleWrapperFactoryModule' + ); + + contextMock.set('contractWrappers', wrappersMock.getMockInstance()); + wrappersMock.set('tokenFactory', tokenFactoryMock.getMockInstance()); + wrappersMock.set('moduleFactory', moduleWrapperFactoryMock.getMockInstance()); + + securityTokenMock = ImportMock.mockClass(contractWrappersModule, 'SecurityToken_3_0_0'); + + tokenFactoryMock.mock( + 'getSecurityTokenInstanceFromTicker', + securityTokenMock.getMockInstance() + ); + + securityTokenMock.mock( + 'canTransfer', + Promise.resolve({ + statusCode: TransferStatusCode.TransferSuccess, + reasonCode: '0x1', + }) + ); + + simpleStoFactoryMock = ImportMock.mockClass(simpleStoFactoryModule, 'SimpleStoFactory'); + + tieredStoFactoryMock = ImportMock.mockClass(tieredStoFactoryModule, 'TieredStoFactory'); + + factoryMockSetup = mockFactories(); + factoryMockSetup.simpleStoFactory = simpleStoFactoryMock.getMockInstance(); + factoryMockSetup.tieredStoFactory = tieredStoFactoryMock.getMockInstance(); + contextMock.set('factories', factoryMockSetup); + + tieredStoMock = ImportMock.mockClass(contractWrappersModule, 'USDTieredSTO_3_1_0'); + simpleSto_3_0_0_Mock = ImportMock.mockClass(contractWrappersModule, 'CappedSTO_3_0_0'); + simpleSto_3_1_0_Mock = ImportMock.mockClass(contractWrappersModule, 'CappedSTO_3_1_0'); + + securityTokenId = SecurityToken.generateId({ symbol: simpleParams.symbol }); + + moduleWrapperFactoryMock.mock('getModuleInstance', simpleSto_3_1_0_Mock.getMockInstance()); + + simpleSto_3_1_0_Mock.mock( + 'getSTODetails', + Promise.resolve({ + totalTokensSold: amountOfTokens, + cap: new BigNumber(2), + }) + ); + + tieredStoMock.mock( + 'getSTODetails', + Promise.resolve({ + tokensSold: amountOfTokens, + capPerTier: [new BigNumber(2), new BigNumber(3)], + }) + ); + + simpleSto_3_1_0_Mock.mock('isFinalized', Promise.resolve(false)); + tieredStoMock.mock('isFinalized', Promise.resolve(false)); + + wrappersMock.mock('getTreasuryWallet', Promise.resolve(treasuryWallet)); + + // Instantiate FinalizeSto with a simple sto + target = new FinalizeSto(simpleParams, contextMock.getMockInstance()); + }); + + afterEach(() => { + restore(); + }); + + describe('Types', () => { + test('should extend procedure and have FinalizeSto type', async () => { + expect(target instanceof Procedure).toBe(true); + expect(target.type).toBe(ProcedureType.FinalizeSto); + }); + }); + + describe('FinalizeSto', () => { + test('should add the transaction to the queue to finalize a simple sto with version 3_1_0', async () => { + const addTransactionSpy = spy(target, 'addTransaction'); + simpleSto_3_1_0_Mock.mock('finalize', Promise.resolve('Finalize')); + + // Real call + await target.prepareTransactions(); + + // Verifications + expect( + addTransactionSpy.getCall(0).calledWith(simpleSto_3_1_0_Mock.getMockInstance().finalize) + ).toEqual(true); + expect(addTransactionSpy.getCall(0).lastArg.tag).toEqual(PolyTransactionTag.FinalizeSto); + expect(addTransactionSpy.callCount).toEqual(1); + }); + + test('should add the transaction to the queue to finalize a tiered sto', async () => { + target = new FinalizeSto(tieredParams, contextMock.getMockInstance()); + + moduleWrapperFactoryMock.mock('getModuleInstance', tieredStoMock.getMockInstance()); + + const addTransactionSpy = spy(target, 'addTransaction'); + tieredStoMock.mock('finalize', Promise.resolve('Finalize')); + + // Real call + await target.prepareTransactions(); + + // Verifications\ + expect( + addTransactionSpy.getCall(0).calledWith(tieredStoMock.getMockInstance().finalize) + ).toEqual(true); + expect(addTransactionSpy.getCall(0).lastArg.tag).toEqual(PolyTransactionTag.FinalizeSto); + expect(addTransactionSpy.callCount).toEqual(1); + }); + + test('should throw an error if the simple sto has not been launched or is archived', async () => { + moduleWrapperFactoryMock.mock('getModuleInstance', Promise.resolve(undefined)); + + await expect(target.prepareTransactions()).rejects.toThrow( + new PolymathError({ + code: ErrorCode.InvalidAddress, + message: `STO ${simpleParams.stoAddress} is either archived or hasn't been launched`, + }) + ); + }); + + test('should throw an error if the tiered sto has not been launched or is archived', async () => { + target = new FinalizeSto(tieredParams, contextMock.getMockInstance()); + + moduleWrapperFactoryMock.mock('getModuleInstance', Promise.resolve(undefined)); + + await expect(target.prepareTransactions()).rejects.toThrow( + new PolymathError({ + code: ErrorCode.InvalidAddress, + message: `STO ${tieredParams.stoAddress} is either archived or hasn't been launched`, + }) + ); + }); + + test('should throw if there is an invalid sto address', async () => { + target = new FinalizeSto( + { + ...simpleParams, + stoAddress: 'invalid', + }, + contextMock.getMockInstance() + ); + await expect(target.prepareTransactions()).rejects.toThrow( + new PolymathError({ + code: ErrorCode.InvalidAddress, + message: `Invalid STO address invalid`, + }) + ); + }); + + test('should throw if there is no valid security token supplied', async () => { + tokenFactoryMock + .mock('getSecurityTokenInstanceFromTicker') + .withArgs(simpleParams.symbol) + .throws(); + + await expect(target.prepareTransactions()).rejects.toThrow( + new PolymathError({ + code: ErrorCode.ProcedureValidationError, + message: `There is no Security Token with symbol ${simpleParams.symbol}`, + }) + ); + }); + + test('should throw if there is an invalid sto type', async () => { + target = new FinalizeSto( + { + ...simpleParams, + stoType: invalidSto as StoType, + }, + contextMock.getMockInstance() + ); + await expect(target.prepareTransactions()).rejects.toThrow( + new PolymathError({ + code: ErrorCode.ProcedureValidationError, + message: `Invalid STO type ${invalidSto}`, + }) + ); + }); + + test('should throw an error if the sto is already finalized', async () => { + simpleSto_3_1_0_Mock.mock('isFinalized', Promise.resolve(true)); + + await expect(target.prepareTransactions()).rejects.toThrow( + new PolymathError({ + code: ErrorCode.InvalidAddress, + message: `STO ${simpleParams.stoAddress} has already been finalized`, + }) + ); + }); + + // This test will change once canTransfer is refactored in project + test('should throw an error if can transfer returns null', async () => { + securityTokenMock.mock('canTransfer', Promise.resolve(undefined)); + + await expect(target.prepareTransactions()).rejects.toThrow( + new PolymathError({ + code: ErrorCode.InvalidAddress, + message: `Treasury wallet "${treasuryWallet}" is not cleared to receive the remaining ${amountOfTokens} "${ + simpleParams.symbol + }" tokens. Please review transfer restrictions regarding this wallet address before attempting to finalize the STO`, + }) + ); + }); + + test('should successfully refresh the simple sto factory in its resolver method', async () => { + const refreshStub = simpleStoFactoryMock.mock('refresh', Promise.resolve()); + await finalizeStoModule.createRefreshStoFactoryResolver( + factoryMockSetup, + simpleParams.symbol, + simpleParams.stoType, + simpleParams.stoAddress + )(); + expect( + refreshStub.getCall(0).calledWithExactly( + SimpleSto.generateId({ + securityTokenId, + stoType: StoType.Simple, + address: simpleParams.stoAddress, + }) + ) + ).toEqual(true); + expect(refreshStub.callCount).toEqual(1); + }); + + test('should successfully refresh the tiered sto factory in its resolver method', async () => { + target = new FinalizeSto(tieredParams, contextMock.getMockInstance()); + const refreshStub = tieredStoFactoryMock.mock('refresh', Promise.resolve()); + await finalizeStoModule.createRefreshStoFactoryResolver( + factoryMockSetup, + tieredParams.symbol, + tieredParams.stoType, + tieredParams.stoAddress + )(); + expect( + refreshStub.getCall(0).calledWithExactly( + TieredSto.generateId({ + securityTokenId, + stoType: StoType.Tiered, + address: tieredParams.stoAddress, + }) + ) + ).toEqual(true); + expect(refreshStub.callCount).toEqual(1); + }); + }); +}); diff --git a/src/procedures/__tests__/TogglePauseSto.ts b/src/procedures/__tests__/TogglePauseSto.ts index d22fa77..3f462f1 100644 --- a/src/procedures/__tests__/TogglePauseSto.ts +++ b/src/procedures/__tests__/TogglePauseSto.ts @@ -37,7 +37,7 @@ const simpleParams: TogglePauseStoProcedureArgs = { const invalidSto = 'InvalidSto'; -describe('PauseSto', () => { +describe('TogglePauseSto', () => { let target: TogglePauseSto; let contextMock: MockManager; let wrappersMock: MockManager; From 8679205447e45f8af932c8c961e91ca2421fb225 Mon Sep 17 00:00:00 2001 From: Victor Wiebe Date: Thu, 19 Dec 2019 06:07:54 -0800 Subject: [PATCH 04/33] refactor: add prettier to file --- src/procedures/__tests__/FinalizeSto.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/procedures/__tests__/FinalizeSto.ts b/src/procedures/__tests__/FinalizeSto.ts index 4195a74..2bcd1b9 100644 --- a/src/procedures/__tests__/FinalizeSto.ts +++ b/src/procedures/__tests__/FinalizeSto.ts @@ -1,7 +1,7 @@ import { ImportMock, MockManager } from 'ts-mock-imports'; import { restore, spy } from 'sinon'; import * as contractWrappersModule from '@polymathnetwork/contract-wrappers'; -import { BigNumber } from '@polymathnetwork/contract-wrappers'; +import { BigNumber, ContractVersion } from '@polymathnetwork/contract-wrappers'; import { TransferStatusCode } from '@polymathnetwork/contract-wrappers'; import { FinalizeSto } from '../FinalizeSto'; import { Procedure } from '../Procedure'; From 7897d90f2910ca57764739990079c0df427173fb Mon Sep 17 00:00:00 2001 From: Victor Wiebe Date: Thu, 19 Dec 2019 06:08:41 -0800 Subject: [PATCH 05/33] test: that a 3_0_0 version contract fails in finalize sto --- src/procedures/__tests__/FinalizeSto.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/procedures/__tests__/FinalizeSto.ts b/src/procedures/__tests__/FinalizeSto.ts index 2bcd1b9..a3330bb 100644 --- a/src/procedures/__tests__/FinalizeSto.ts +++ b/src/procedures/__tests__/FinalizeSto.ts @@ -260,6 +260,19 @@ describe('FinalizeSto', () => { ); }); + test('should throw error if the simple sto version is 3_0_0', async () => { + moduleWrapperFactoryMock.mock('getModuleInstance', simpleSto_3_0_0_Mock.getMockInstance()); + simpleSto_3_0_0_Mock.set('contractVersion', ContractVersion.V3_0_0); + + await expect(target.prepareTransactions()).rejects.toThrow( + new PolymathError({ + code: ErrorCode.IncorrectVersion, + message: + 'Capped STO version is 3.0.0. Version 3.1.0 or greater is required for forced finalization', + }) + ); + }); + // This test will change once canTransfer is refactored in project test('should throw an error if can transfer returns null', async () => { securityTokenMock.mock('canTransfer', Promise.resolve(undefined)); From 5966f3abfcf79ad4259ebcc2c6e22e4175e0ea5d Mon Sep 17 00:00:00 2001 From: Victor Wiebe Date: Thu, 19 Dec 2019 10:16:35 -0800 Subject: [PATCH 06/33] refactor: move resolver into its own function --- src/procedures/InvestInSimpleSto.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/procedures/InvestInSimpleSto.ts b/src/procedures/InvestInSimpleSto.ts index 55e29c0..a18645a 100644 --- a/src/procedures/InvestInSimpleSto.ts +++ b/src/procedures/InvestInSimpleSto.ts @@ -21,6 +21,13 @@ export const createRefreshSecurityTokenFactoryResolver = ( return factories.securityTokenFactory.refresh(securityTokenId); }; +export const createRefreshSimpleStoFactoryResolver = ( + factories: Factories, + simpleStoId: string +) => async () => { + return factories.simpleStoFactory.refresh(simpleStoId); +}; + export class InvestInSimpleSto extends Procedure { public type = ProcedureType.InvestInSimpleSto; @@ -111,9 +118,7 @@ export class InvestInSimpleSto extends Procedure } const resolvers = [ - async () => { - return factories.simpleStoFactory.refresh(simpleStoId); - }, + createRefreshSimpleStoFactoryResolver(factories, simpleStoId), createRefreshSecurityTokenFactoryResolver(factories, securityTokenId), ]; From 08c5657a13819ca69cdca3d8fc27095a46b1e082 Mon Sep 17 00:00:00 2001 From: Victor Wiebe Date: Thu, 19 Dec 2019 10:18:11 -0800 Subject: [PATCH 07/33] test: investInSimpleSto basic tests --- src/procedures/__tests__/InvestInSimpleSto.ts | 255 ++++++++++++++++++ 1 file changed, 255 insertions(+) create mode 100644 src/procedures/__tests__/InvestInSimpleSto.ts diff --git a/src/procedures/__tests__/InvestInSimpleSto.ts b/src/procedures/__tests__/InvestInSimpleSto.ts new file mode 100644 index 0000000..ec89797 --- /dev/null +++ b/src/procedures/__tests__/InvestInSimpleSto.ts @@ -0,0 +1,255 @@ +import { ImportMock, MockManager } from 'ts-mock-imports'; +import { restore, spy } from 'sinon'; +import * as contractWrappersModule from '@polymathnetwork/contract-wrappers'; +import { BigNumber, TransferStatusCode } from '@polymathnetwork/contract-wrappers'; +import { InvestInSimpleSto } from '../InvestInSimpleSto'; +import { Procedure } from '../Procedure'; +import { PolymathError } from '../../PolymathError'; +import { + Currency, + ErrorCode, + InvestInSimpleStoProcedureArgs, + PolyTransactionTag, + ProcedureType, + StoType, +} from '../../types'; +import * as investInSimpleStoModule from '../InvestInSimpleSto'; +import * as simpleStoFactoryModule from '../../entities/factories/SimpleStoFactory'; +import * as contextModule from '../../Context'; +import * as wrappersModule from '../../PolymathBase'; +import * as tokenFactoryModule from '../../testUtils/MockedTokenFactoryModule'; +import * as moduleWrapperFactoryModule from '../../testUtils/MockedModuleWrapperFactoryModule'; +import { mockFactories } from '../../testUtils/mockFactories'; +import { Factories } from '../../Context'; +import { SimpleSto, SecurityToken } from '../../entities'; +import * as securityTokenFactoryModule from '../../entities/factories/SecurityTokenFactory'; +import { Wallet } from '../../Wallet'; + +const simpleParams: InvestInSimpleStoProcedureArgs = { + symbol: 'TEST1', + stoAddress: '0x5555555555555555555555555555555555555555', + amount: new BigNumber(1), +}; + +const simpleStoObject = { + isFinalized: false, + isPaused: false, + startDate: new Date(2010, 1), + beneficialInvestmentsAllowed: false, + fundraiseCurrencies: [Currency.ETH], +}; + +const treasuryWallet = '0x1111111111111111111111111111111111111111'; +const currentWalletAddress = '0x2222222222222222222222222222222222222222'; + +describe('InvestInSimpleSto', () => { + let target: InvestInSimpleSto; + let contextMock: MockManager; + let wrappersMock: MockManager; + let securityTokenFactoryMock: MockManager; + let tokenFactoryMock: MockManager; + let moduleWrapperFactoryMock: MockManager< + moduleWrapperFactoryModule.MockedModuleWrapperFactoryModule + >; + + let simpleStoMock: MockManager; + + // Mock factories + let simpleStoFactoryMock: MockManager; + + let securityTokenMock: MockManager; + + let factoryMockSetup: Factories; + let securityTokenId: string; + let simpleStoId: string; + + beforeEach(() => { + // Mock the context, wrappers, and tokenFactory to test InvestInSimpleSto + contextMock = ImportMock.mockClass(contextModule, 'Context'); + wrappersMock = ImportMock.mockClass(wrappersModule, 'PolymathBase'); + tokenFactoryMock = ImportMock.mockClass(tokenFactoryModule, 'MockedTokenFactoryModule'); + moduleWrapperFactoryMock = ImportMock.mockClass( + moduleWrapperFactoryModule, + 'MockedModuleWrapperFactoryModule' + ); + + contextMock.set('contractWrappers', wrappersMock.getMockInstance()); + wrappersMock.set('tokenFactory', tokenFactoryMock.getMockInstance()); + wrappersMock.set('moduleFactory', moduleWrapperFactoryMock.getMockInstance()); + + securityTokenMock = ImportMock.mockClass(contractWrappersModule, 'SecurityToken_3_0_0'); + + securityTokenFactoryMock = ImportMock.mockClass( + securityTokenFactoryModule, + 'SecurityTokenFactory' + ); + + tokenFactoryMock.mock( + 'getSecurityTokenInstanceFromTicker', + securityTokenMock.getMockInstance() + ); + + securityTokenMock.mock( + 'canTransfer', + Promise.resolve({ + statusCode: TransferStatusCode.TransferSuccess, + reasonCode: '0x1', + }) + ); + + simpleStoFactoryMock = ImportMock.mockClass(simpleStoFactoryModule, 'SimpleStoFactory'); + + factoryMockSetup = mockFactories(); + factoryMockSetup.simpleStoFactory = simpleStoFactoryMock.getMockInstance(); + factoryMockSetup.securityTokenFactory = securityTokenFactoryMock.getMockInstance(); + contextMock.set('factories', factoryMockSetup); + + simpleStoMock = ImportMock.mockClass(contractWrappersModule, 'CappedSTO_3_1_0'); + + securityTokenId = SecurityToken.generateId({ symbol: simpleParams.symbol }); + simpleStoId = SimpleSto.generateId({ + securityTokenId, + stoType: StoType.Simple, + address: simpleParams.stoAddress, + }); + + moduleWrapperFactoryMock.mock('getModuleInstance', simpleStoMock.getMockInstance()); + contextMock.set( + 'currentWallet', + new Wallet({ address: () => Promise.resolve(currentWalletAddress) }) + ); + + simpleStoMock.mock('isFinalized', Promise.resolve(false)); + + simpleStoFactoryMock.mock('fetch', simpleStoObject); + + wrappersMock.mock('getTreasuryWallet', Promise.resolve(treasuryWallet)); + + // Instantiate InvestInSimpleSto with a simple sto + target = new InvestInSimpleSto(simpleParams, contextMock.getMockInstance()); + }); + + afterEach(() => { + restore(); + }); + + describe('Types', () => { + test('should extend procedure and have InvestInSimpleSto type', async () => { + expect(target instanceof Procedure).toBe(true); + expect(target.type).toBe(ProcedureType.InvestInSimpleSto); + }); + }); + + describe('InvestInSimpleSto', () => { + test('should add a transaction to the queue to invest in a simple sto', async () => { + const addTransactionSpy = spy(target, 'addTransaction'); + simpleStoMock.mock('buyTokens', Promise.resolve('BuyTokens')); + + // Real call + await target.prepareTransactions(); + + // Verifications + expect( + addTransactionSpy.getCall(0).calledWith(simpleStoMock.getMockInstance().buyTokens) + ).toEqual(true); + expect(addTransactionSpy.getCall(0).lastArg.tag).toEqual(PolyTransactionTag.BuyTokens); + expect(addTransactionSpy.callCount).toEqual(1); + }); + + test('should throw an error if the simple sto has not been launched or is archived', async () => { + moduleWrapperFactoryMock.mock('getModuleInstance', Promise.resolve(undefined)); + + await expect(target.prepareTransactions()).rejects.toThrow( + new PolymathError({ + code: ErrorCode.InvalidAddress, + message: `STO ${simpleParams.stoAddress} is either archived or hasn't been launched`, + }) + ); + }); + + test('should throw if there is an invalid sto address', async () => { + target = new InvestInSimpleSto( + { + ...simpleParams, + stoAddress: 'invalid', + }, + contextMock.getMockInstance() + ); + await expect(target.prepareTransactions()).rejects.toThrow( + new PolymathError({ + code: ErrorCode.InvalidAddress, + message: `Invalid STO address invalid`, + }) + ); + }); + + test('should throw if there is no valid security token supplied', async () => { + tokenFactoryMock + .mock('getSecurityTokenInstanceFromTicker') + .withArgs(simpleParams.symbol) + .throws(); + + await expect(target.prepareTransactions()).rejects.toThrow( + new PolymathError({ + code: ErrorCode.ProcedureValidationError, + message: `There is no Security Token with symbol ${simpleParams.symbol}`, + }) + ); + }); + + test('should throw an error if the sto is already finalized', async () => { + simpleStoFactoryMock.mock('fetch', { ...simpleStoObject, isFinalized: true }); + + await expect(target.prepareTransactions()).rejects.toThrow( + new PolymathError({ + code: ErrorCode.ProcedureValidationError, + message: `STO ${simpleParams.stoAddress} has already been finalized`, + }) + ); + }); + + test('should throw an error if the start date is in the future', async () => { + simpleStoFactoryMock.mock('fetch', { ...simpleStoObject, startDate: new Date(2040, 0) }); + + await expect(target.prepareTransactions()).rejects.toThrow( + new PolymathError({ + code: ErrorCode.ProcedureValidationError, + message: `Cannot invest in STO ${simpleParams.stoAddress} because it hasn't started yet`, + }) + ); + }); + + test('should throw an error if the sto is paused', async () => { + simpleStoFactoryMock.mock('fetch', { ...simpleStoObject, isPaused: true }); + + await expect(target.prepareTransactions()).rejects.toThrow( + new PolymathError({ + code: ErrorCode.ProcedureValidationError, + message: `STO ${simpleParams.stoAddress} is paused`, + }) + ); + }); + + test('should successfully refresh the simple sto factory with its resolver method', async () => { + const refreshStub = simpleStoFactoryMock.mock('refresh', Promise.resolve()); + await investInSimpleStoModule.createRefreshSimpleStoFactoryResolver( + factoryMockSetup, + simpleStoId + )(); + + expect(refreshStub.getCall(0).calledWithExactly(simpleStoId)).toEqual(true); + expect(refreshStub.callCount).toEqual(1); + }); + + test('should successfully refresh the security token factory with its resolver method', async () => { + const refreshStub = securityTokenFactoryMock.mock('refresh', Promise.resolve()); + await investInSimpleStoModule.createRefreshSecurityTokenFactoryResolver( + factoryMockSetup, + securityTokenId + )(); + + expect(refreshStub.getCall(0).calledWithExactly(securityTokenId)).toEqual(true); + expect(refreshStub.callCount).toEqual(1); + }); + }); +}); From d46384a3b27d508157b52791ef159665efc470fd Mon Sep 17 00:00:00 2001 From: Victor Wiebe Date: Thu, 19 Dec 2019 10:58:47 -0800 Subject: [PATCH 08/33] test: beneficial investments allowed rule in procedure --- src/procedures/__tests__/InvestInSimpleSto.ts | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/procedures/__tests__/InvestInSimpleSto.ts b/src/procedures/__tests__/InvestInSimpleSto.ts index ec89797..9518ac9 100644 --- a/src/procedures/__tests__/InvestInSimpleSto.ts +++ b/src/procedures/__tests__/InvestInSimpleSto.ts @@ -35,12 +35,13 @@ const simpleStoObject = { isFinalized: false, isPaused: false, startDate: new Date(2010, 1), - beneficialInvestmentsAllowed: false, + beneficialInvestmentsAllowed: true, fundraiseCurrencies: [Currency.ETH], }; const treasuryWallet = '0x1111111111111111111111111111111111111111'; const currentWalletAddress = '0x2222222222222222222222222222222222222222'; +const beneficiaryAddress = '0x3333333333333333333333333333333333333333'; describe('InvestInSimpleSto', () => { let target: InvestInSimpleSto; @@ -219,6 +220,24 @@ describe('InvestInSimpleSto', () => { ); }); + test('should throw an error if beneficial investments not allowed, and the parameters include a beneficiary address', async () => { + target = new InvestInSimpleSto( + { ...simpleParams, beneficiary: beneficiaryAddress }, + contextMock.getMockInstance() + ); + simpleStoFactoryMock.mock('fetch', { + ...simpleStoObject, + beneficialInvestmentsAllowed: false, + }); + + await expect(target.prepareTransactions()).rejects.toThrow( + new PolymathError({ + code: ErrorCode.ProcedureValidationError, + message: `Cannot invest on behalf of ${beneficiaryAddress} because this STO doesn't allow beneficial investments`, + }) + ); + }); + test('should throw an error if the sto is paused', async () => { simpleStoFactoryMock.mock('fetch', { ...simpleStoObject, isPaused: true }); From b19f2676257932e7ed8fa09b69485ccd71163cda Mon Sep 17 00:00:00 2001 From: Victor Wiebe Date: Thu, 19 Dec 2019 19:00:04 -0800 Subject: [PATCH 09/33] test: complete full test coverage --- src/procedures/__tests__/InvestInSimpleSto.ts | 91 ++++++++++++++++++- 1 file changed, 87 insertions(+), 4 deletions(-) diff --git a/src/procedures/__tests__/InvestInSimpleSto.ts b/src/procedures/__tests__/InvestInSimpleSto.ts index 9518ac9..1471e99 100644 --- a/src/procedures/__tests__/InvestInSimpleSto.ts +++ b/src/procedures/__tests__/InvestInSimpleSto.ts @@ -24,6 +24,7 @@ import { Factories } from '../../Context'; import { SimpleSto, SecurityToken } from '../../entities'; import * as securityTokenFactoryModule from '../../entities/factories/SecurityTokenFactory'; import { Wallet } from '../../Wallet'; +import { ApproveErc20 } from '~/procedures'; const simpleParams: InvestInSimpleStoProcedureArgs = { symbol: 'TEST1', @@ -42,6 +43,7 @@ const simpleStoObject = { const treasuryWallet = '0x1111111111111111111111111111111111111111'; const currentWalletAddress = '0x2222222222222222222222222222222222222222'; const beneficiaryAddress = '0x3333333333333333333333333333333333333333'; +const polyTokenAddress = '0x8888888888888888888888888888888888888888'; describe('InvestInSimpleSto', () => { let target: InvestInSimpleSto; @@ -52,7 +54,7 @@ describe('InvestInSimpleSto', () => { let moduleWrapperFactoryMock: MockManager< moduleWrapperFactoryModule.MockedModuleWrapperFactoryModule >; - + let polyTokenMock: MockManager; let simpleStoMock: MockManager; // Mock factories @@ -98,6 +100,13 @@ describe('InvestInSimpleSto', () => { }) ); + polyTokenMock = ImportMock.mockClass(contractWrappersModule, 'PolyToken'); + polyTokenMock.mock('balanceOf', new BigNumber(10)); + polyTokenMock.mock('address', polyTokenAddress); + polyTokenMock.mock('allowance', new BigNumber(10)); + wrappersMock.mock('isTestnet', false); + wrappersMock.set('polyToken', polyTokenMock.getMockInstance()); + simpleStoFactoryMock = ImportMock.mockClass(simpleStoFactoryModule, 'SimpleStoFactory'); factoryMockSetup = mockFactories(); @@ -142,6 +151,33 @@ describe('InvestInSimpleSto', () => { }); describe('InvestInSimpleSto', () => { + test('should add a transaction to the queue to invest in a simple sto with poly', async () => { + simpleStoFactoryMock.mock('fetch', { + ...simpleStoObject, + beneficialInvestmentsAllowed: false, + fundraiseCurrencies: [Currency.POLY], + }); + const addTransactionSpy = spy(target, 'addTransaction'); + const addProcedureSpy = spy(target, 'addProcedure'); + simpleStoMock.mock('buyTokensWithPoly', Promise.resolve('BuyTokensWithPoly')); + + securityTokenMock.mock('balanceOf', Promise.resolve(new BigNumber(10))); + + // Real call + await target.prepareTransactions(); + + // Verifications + expect( + addTransactionSpy.getCall(0).calledWith(simpleStoMock.getMockInstance().buyTokensWithPoly) + ).toEqual(true); + expect(addTransactionSpy.getCall(0).lastArg.tag).toEqual( + PolyTransactionTag.BuyTokensWithPoly + ); + expect(addTransactionSpy.callCount).toEqual(1); + expect(addProcedureSpy.getCall(0).calledWithExactly(ApproveErc20)).toEqual(true); + expect(addProcedureSpy.callCount).toEqual(1); + }); + test('should add a transaction to the queue to invest in a simple sto', async () => { const addTransactionSpy = spy(target, 'addTransaction'); simpleStoMock.mock('buyTokens', Promise.resolve('BuyTokens')); @@ -157,6 +193,44 @@ describe('InvestInSimpleSto', () => { expect(addTransactionSpy.callCount).toEqual(1); }); + test('should add a transaction to the queue to invest in a simple sto on the behalf of a beneficiary', async () => { + target = new InvestInSimpleSto( + { ...simpleParams, beneficiary: beneficiaryAddress }, + contextMock.getMockInstance() + ); + const addTransactionSpy = spy(target, 'addTransaction'); + simpleStoMock.mock('buyTokens', Promise.resolve('BuyTokens')); + + // Real call + await target.prepareTransactions(); + + // Verifications + expect( + addTransactionSpy.getCall(0).calledWith(simpleStoMock.getMockInstance().buyTokens) + ).toEqual(true); + expect(addTransactionSpy.getCall(0).lastArg.tag).toEqual(PolyTransactionTag.BuyTokens); + expect(addTransactionSpy.callCount).toEqual(1); + }); + + test('should throw an error as a non eth currency sto does not support investing on behalf of another party', async () => { + target = new InvestInSimpleSto( + { ...simpleParams, beneficiary: beneficiaryAddress }, + contextMock.getMockInstance() + ); + simpleStoFactoryMock.mock('fetch', { + ...simpleStoObject, + beneficialInvestmentsAllowed: true, + fundraiseCurrencies: [Currency.POLY], + }); + + await expect(target.prepareTransactions()).rejects.toThrow( + new PolymathError({ + code: ErrorCode.ProcedureValidationError, + message: `This STO does not support investing in POLY on behalf of someone else`, + }) + ); + }); + test('should throw an error if the simple sto has not been launched or is archived', async () => { moduleWrapperFactoryMock.mock('getModuleInstance', Promise.resolve(undefined)); @@ -199,7 +273,10 @@ describe('InvestInSimpleSto', () => { }); test('should throw an error if the sto is already finalized', async () => { - simpleStoFactoryMock.mock('fetch', { ...simpleStoObject, isFinalized: true }); + simpleStoFactoryMock.mock('fetch', { + ...simpleStoObject, + isFinalized: true, + }); await expect(target.prepareTransactions()).rejects.toThrow( new PolymathError({ @@ -210,7 +287,10 @@ describe('InvestInSimpleSto', () => { }); test('should throw an error if the start date is in the future', async () => { - simpleStoFactoryMock.mock('fetch', { ...simpleStoObject, startDate: new Date(2040, 0) }); + simpleStoFactoryMock.mock('fetch', { + ...simpleStoObject, + startDate: new Date(2040, 0), + }); await expect(target.prepareTransactions()).rejects.toThrow( new PolymathError({ @@ -239,7 +319,10 @@ describe('InvestInSimpleSto', () => { }); test('should throw an error if the sto is paused', async () => { - simpleStoFactoryMock.mock('fetch', { ...simpleStoObject, isPaused: true }); + simpleStoFactoryMock.mock('fetch', { + ...simpleStoObject, + isPaused: true, + }); await expect(target.prepareTransactions()).rejects.toThrow( new PolymathError({ From 59ba9e55167dd8befe5747d792cc4094de5b4c95 Mon Sep 17 00:00:00 2001 From: Victor Wiebe Date: Fri, 20 Dec 2019 08:32:07 -0800 Subject: [PATCH 10/33] fix: descriptions and unnecessary code in two procedures --- src/procedures/__tests__/InvestInSimpleSto.ts | 10 +++++----- src/procedures/__tests__/LaunchSimpleSto.ts | 2 -- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/procedures/__tests__/InvestInSimpleSto.ts b/src/procedures/__tests__/InvestInSimpleSto.ts index 1471e99..e74dc34 100644 --- a/src/procedures/__tests__/InvestInSimpleSto.ts +++ b/src/procedures/__tests__/InvestInSimpleSto.ts @@ -24,7 +24,7 @@ import { Factories } from '../../Context'; import { SimpleSto, SecurityToken } from '../../entities'; import * as securityTokenFactoryModule from '../../entities/factories/SecurityTokenFactory'; import { Wallet } from '../../Wallet'; -import { ApproveErc20 } from '~/procedures'; +import { ApproveErc20 } from '../../procedures'; const simpleParams: InvestInSimpleStoProcedureArgs = { symbol: 'TEST1', @@ -231,7 +231,7 @@ describe('InvestInSimpleSto', () => { ); }); - test('should throw an error if the simple sto has not been launched or is archived', async () => { + test('should throw an error if the simple sto has not yet been launched or is archived', async () => { moduleWrapperFactoryMock.mock('getModuleInstance', Promise.resolve(undefined)); await expect(target.prepareTransactions()).rejects.toThrow( @@ -272,7 +272,7 @@ describe('InvestInSimpleSto', () => { ); }); - test('should throw an error if the sto is already finalized', async () => { + test('should throw an error if the sto has already been finalized', async () => { simpleStoFactoryMock.mock('fetch', { ...simpleStoObject, isFinalized: true, @@ -286,7 +286,7 @@ describe('InvestInSimpleSto', () => { ); }); - test('should throw an error if the start date is in the future', async () => { + test('should throw an error if the sto start date is in the future', async () => { simpleStoFactoryMock.mock('fetch', { ...simpleStoObject, startDate: new Date(2040, 0), @@ -300,7 +300,7 @@ describe('InvestInSimpleSto', () => { ); }); - test('should throw an error if beneficial investments not allowed, and the parameters include a beneficiary address', async () => { + test('should throw an error if beneficial investments are not allowed and the parameters include a beneficiary address', async () => { target = new InvestInSimpleSto( { ...simpleParams, beneficiary: beneficiaryAddress }, contextMock.getMockInstance() diff --git a/src/procedures/__tests__/LaunchSimpleSto.ts b/src/procedures/__tests__/LaunchSimpleSto.ts index 60a9c29..0651878 100644 --- a/src/procedures/__tests__/LaunchSimpleSto.ts +++ b/src/procedures/__tests__/LaunchSimpleSto.ts @@ -3,7 +3,6 @@ import { restore, spy } from 'sinon'; import * as contractWrappersModule from '@polymathnetwork/contract-wrappers'; import { BigNumber, - CappedSTOFundRaiseType as CappedStoCurrency, SecurityTokenEvents, TransactionReceiptWithDecodedLogs, } from '@polymathnetwork/contract-wrappers'; @@ -28,7 +27,6 @@ import { Wallet } from '../../Wallet'; import { TransferErc20 } from '..'; import { mockFactories } from '../../testUtils/mockFactories'; import { SimpleSto, SecurityToken } from '../../entities'; -import { PostTransactionResolver } from '../../PostTransactionResolver'; const params: LaunchSimpleStoProcedureArgs = { symbol: 'TEST1', From dffbf3a746957bea0fc50d8725a025c0b1182558 Mon Sep 17 00:00:00 2001 From: Victor Wiebe Date: Fri, 20 Dec 2019 08:52:50 -0800 Subject: [PATCH 11/33] test: resolver method in createCheckpoint, missing --- src/procedures/__tests__/CreateCheckpoint.ts | 34 +++++++++++++++++--- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/src/procedures/__tests__/CreateCheckpoint.ts b/src/procedures/__tests__/CreateCheckpoint.ts index 13e269e..7a13707 100644 --- a/src/procedures/__tests__/CreateCheckpoint.ts +++ b/src/procedures/__tests__/CreateCheckpoint.ts @@ -5,16 +5,19 @@ import { TransactionReceiptWithDecodedLogs } from 'ethereum-protocol'; import { BigNumber } from '@polymathnetwork/contract-wrappers'; import { SecurityTokenEvents } from '@polymathnetwork/contract-wrappers'; import { CreateCheckpoint } from '../../procedures/CreateCheckpoint'; +import * as createCheckpointModule from '../../procedures/CreateCheckpoint'; import { Procedure } from '../../procedures/Procedure'; import { PolymathError } from '../../PolymathError'; import { ErrorCode, PolyTransactionTag, ProcedureType } from '../../types'; import * as utilsModule from '../../utils'; import * as checkpointFactoryModule from '../../entities/factories/CheckpointFactory'; +import * as securityTokenFactoryModule from '../../entities/factories/SecurityTokenFactory'; import * as contextModule from '../../Context'; import * as wrappersModule from '../../PolymathBase'; import * as tokenFactoryModule from '../../testUtils/MockedTokenFactoryModule'; import { mockFactories } from '../../testUtils/mockFactories'; import { Checkpoint, SecurityToken } from '../../entities'; +import { Factories } from '../../Context'; const params = { symbol: 'TEST1', @@ -32,9 +35,12 @@ describe('CreateCheckpoint', () => { let wrappersMock: MockManager; let tokenFactoryMock: MockManager; + let factoryMockSetup: Factories; + let securityTokenId: string; // Mock factories let checkpointFactoryMock: MockManager; + let securityTokenFactoryMock: MockManager; beforeEach(() => { // Mock the context, wrappers, and tokenFactory to test CreateCheckpoint @@ -52,10 +58,19 @@ describe('CreateCheckpoint', () => { wrappersMock.set('tokenFactory', tokenFactoryMock.getMockInstance()); checkpointFactoryMock = ImportMock.mockClass(checkpointFactoryModule, 'CheckpointFactory'); - const factoryMockSetup = mockFactories(); + securityTokenFactoryMock = ImportMock.mockClass( + securityTokenFactoryModule, + 'SecurityTokenFactory' + ); + factoryMockSetup = mockFactories(); factoryMockSetup.checkpointFactory = checkpointFactoryMock.getMockInstance(); + factoryMockSetup.securityTokenFactory = securityTokenFactoryMock.getMockInstance(); contextMock.set('factories', factoryMockSetup); + securityTokenId = SecurityToken.generateId({ + symbol: params.symbol, + }); + // Instantiate CreateCheckpoint target = new CreateCheckpoint(params, contextMock.getMockInstance()); }); @@ -105,7 +120,7 @@ describe('CreateCheckpoint', () => { test('should return the newly created checkpoint', async () => { const indexValue = 1; const checkpointObject = { - securityTokenId: () => params.symbol, + securityTokenId: () => securityTokenId, index: () => indexValue, }; @@ -129,9 +144,7 @@ describe('CreateCheckpoint', () => { expect( fetchStub.getCall(0).calledWithExactly( Checkpoint.generateId({ - securityTokenId: SecurityToken.generateId({ - symbol: params.symbol, - }), + securityTokenId, index: indexValue, }) ) @@ -159,5 +172,16 @@ describe('CreateCheckpoint', () => { }) ); }); + + test('should successfully refresh the security token factory with its resolver method', async () => { + const refreshStub = securityTokenFactoryMock.mock('refresh', Promise.resolve()); + await createCheckpointModule.createRefreshSecurityTokenFactoryResolver( + factoryMockSetup, + securityTokenId + )(); + + expect(refreshStub.getCall(0).calledWithExactly(securityTokenId)).toEqual(true); + expect(refreshStub.callCount).toEqual(1); + }); }); }); From 588cbf936d2a6232dc818270766200e6a180eb8c Mon Sep 17 00:00:00 2001 From: Victor Wiebe Date: Fri, 20 Dec 2019 10:48:07 -0800 Subject: [PATCH 12/33] refactor: experimental change for reducing bloat of code --- src/procedures/InvestInTieredSto.ts | 66 ++++++++++++++--------------- 1 file changed, 32 insertions(+), 34 deletions(-) diff --git a/src/procedures/InvestInTieredSto.ts b/src/procedures/InvestInTieredSto.ts index fdfcc54..c7c279e 100644 --- a/src/procedures/InvestInTieredSto.ts +++ b/src/procedures/InvestInTieredSto.ts @@ -1,13 +1,13 @@ -import { ModuleName, BigNumber } from '@polymathnetwork/contract-wrappers'; +import { BigNumber, FundRaiseType, ModuleName } from '@polymathnetwork/contract-wrappers'; import { Procedure } from './Procedure'; import { - ProcedureType, - PolyTransactionTag, - InvestInTieredStoProcedureArgs, - ErrorCode, - StoType, Currency, + ErrorCode, + InvestInTieredStoProcedureArgs, isInvestWithStableCoinArgs, + PolyTransactionTag, + ProcedureType, + StoType, } from '../types'; import { PolymathError } from '../PolymathError'; import { isValidAddress } from '../utils'; @@ -22,6 +22,13 @@ export const createRefreshSecurityTokenFactoryResolver = ( return factories.securityTokenFactory.refresh(securityTokenId); }; +export const createRefreshTieredStoFactoryResolver = ( + factories: Factories, + simpleStoId: string +) => async () => { + return factories.simpleStoFactory.refresh(simpleStoId); +}; + export class InvestInTieredSto extends Procedure { public type = ProcedureType.InvestInTieredSto; @@ -52,6 +59,13 @@ export class InvestInTieredSto extends Procedure }); } + const securityTokenId = SecurityToken.generateId({ symbol }); + const tieredStoId = TieredSto.generateId({ + securityTokenId, + stoType: StoType.Tiered, + address: stoAddress, + }); + const stoModule = await contractWrappers.moduleFactory.getModuleInstance({ name: ModuleName.UsdTieredSTO, address: stoAddress, @@ -64,25 +78,17 @@ export class InvestInTieredSto extends Procedure }); } - const [ + const [sto, currentAddress] = await Promise.all([ + factories.simpleStoFactory.fetch(tieredStoId), + context.currentWallet.address(), + ]); + const { isFinalized, isPaused, - currentAddress, - beneficialInvestmentsAllowed, startDate, - canRaiseInEth, - canRaiseInPoly, - canRaiseInStableCoin, - ] = await Promise.all([ - stoModule.isFinalized(), - stoModule.paused(), - context.currentWallet.address(), - stoModule.allowBeneficialInvestments(), - stoModule.startTime(), - stoModule.fundRaiseTypes({ type: Currency.ETH }), - stoModule.fundRaiseTypes({ type: Currency.POLY }), - stoModule.fundRaiseTypes({ type: Currency.StableCoin }), - ]); + beneficialInvestmentsAllowed, + fundraiseCurrencies, + } = sto; if (startDate > new Date()) { throw new PolymathError({ @@ -114,16 +120,8 @@ export class InvestInTieredSto extends Procedure beneficiary = beneficiary || currentAddress; - const securityTokenId = SecurityToken.generateId({ symbol }); - const tieredStoId = TieredSto.generateId({ - securityTokenId, - stoType: StoType.Tiered, - address: stoAddress, - }); const resolvers = [ - async () => { - return factories.tieredStoFactory.refresh(tieredStoId); - }, + createRefreshTieredStoFactoryResolver(factories, tieredStoId), createRefreshSecurityTokenFactoryResolver(factories, securityTokenId), ]; @@ -135,7 +133,7 @@ export class InvestInTieredSto extends Procedure if (isInvestWithStableCoinArgs(args)) { const { stableCoinAddress } = args; - if (!canRaiseInStableCoin) { + if (!fundraiseCurrencies.includes(FundRaiseType.StableCoin)) { throw unsupportedCurrencyError; } @@ -155,7 +153,7 @@ export class InvestInTieredSto extends Procedure investedSC: amount, }); } else if (currency === Currency.POLY) { - if (!canRaiseInPoly) { + if (!fundraiseCurrencies.includes(FundRaiseType.POLY)) { throw unsupportedCurrencyError; } @@ -173,7 +171,7 @@ export class InvestInTieredSto extends Procedure investedPOLY: amount, }); } else { - if (!canRaiseInEth) { + if (!fundraiseCurrencies.includes(FundRaiseType.ETH)) { throw unsupportedCurrencyError; } From 7bd86306cc860ffa841cddf5958847676c3a1628 Mon Sep 17 00:00:00 2001 From: Victor Wiebe Date: Fri, 20 Dec 2019 12:09:37 -0800 Subject: [PATCH 13/33] fix: wrong factory type --- src/procedures/InvestInTieredSto.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/procedures/InvestInTieredSto.ts b/src/procedures/InvestInTieredSto.ts index c7c279e..26a8930 100644 --- a/src/procedures/InvestInTieredSto.ts +++ b/src/procedures/InvestInTieredSto.ts @@ -79,7 +79,7 @@ export class InvestInTieredSto extends Procedure } const [sto, currentAddress] = await Promise.all([ - factories.simpleStoFactory.fetch(tieredStoId), + factories.tieredStoFactory.fetch(tieredStoId), context.currentWallet.address(), ]); const { From 5c976874162591c9450014a5f0d07eb730f15d7f Mon Sep 17 00:00:00 2001 From: Victor Wiebe Date: Fri, 20 Dec 2019 14:14:12 -0800 Subject: [PATCH 14/33] test: invest in tiered sto, fix mistakes --- src/procedures/InvestInTieredSto.ts | 4 +- src/procedures/__tests__/InvestInSimpleSto.ts | 8 - src/procedures/__tests__/InvestInTieredSto.ts | 453 ++++++++++++++++++ 3 files changed, 455 insertions(+), 10 deletions(-) create mode 100644 src/procedures/__tests__/InvestInTieredSto.ts diff --git a/src/procedures/InvestInTieredSto.ts b/src/procedures/InvestInTieredSto.ts index 26a8930..c7074be 100644 --- a/src/procedures/InvestInTieredSto.ts +++ b/src/procedures/InvestInTieredSto.ts @@ -24,9 +24,9 @@ export const createRefreshSecurityTokenFactoryResolver = ( export const createRefreshTieredStoFactoryResolver = ( factories: Factories, - simpleStoId: string + tieredStoId: string ) => async () => { - return factories.simpleStoFactory.refresh(simpleStoId); + return factories.tieredStoFactory.refresh(tieredStoId); }; export class InvestInTieredSto extends Procedure { diff --git a/src/procedures/__tests__/InvestInSimpleSto.ts b/src/procedures/__tests__/InvestInSimpleSto.ts index e74dc34..ac14ba2 100644 --- a/src/procedures/__tests__/InvestInSimpleSto.ts +++ b/src/procedures/__tests__/InvestInSimpleSto.ts @@ -92,14 +92,6 @@ describe('InvestInSimpleSto', () => { securityTokenMock.getMockInstance() ); - securityTokenMock.mock( - 'canTransfer', - Promise.resolve({ - statusCode: TransferStatusCode.TransferSuccess, - reasonCode: '0x1', - }) - ); - polyTokenMock = ImportMock.mockClass(contractWrappersModule, 'PolyToken'); polyTokenMock.mock('balanceOf', new BigNumber(10)); polyTokenMock.mock('address', polyTokenAddress); diff --git a/src/procedures/__tests__/InvestInTieredSto.ts b/src/procedures/__tests__/InvestInTieredSto.ts new file mode 100644 index 0000000..37b7878 --- /dev/null +++ b/src/procedures/__tests__/InvestInTieredSto.ts @@ -0,0 +1,453 @@ +import { ImportMock, MockManager } from 'ts-mock-imports'; +import { restore, spy } from 'sinon'; +import * as contractWrappersModule from '@polymathnetwork/contract-wrappers'; +import { BigNumber, FundRaiseType, TransferStatusCode } from '@polymathnetwork/contract-wrappers'; +import * as investInTieredStoModule from '../InvestInTieredSto'; +import { InvestInTieredSto } from '../InvestInTieredSto'; +import { Procedure } from '../Procedure'; +import { PolymathError } from '../../PolymathError'; +import { + Currency, + ErrorCode, + InvestInTieredStoProcedureArgs, + PolyTransactionTag, + ProcedureType, + StoType, +} from '../../types'; +import * as tieredStoFactoryModule from '../../entities/factories/TieredStoFactory'; +import * as contextModule from '../../Context'; +import { Factories } from '../../Context'; +import * as wrappersModule from '../../PolymathBase'; +import * as tokenFactoryModule from '../../testUtils/MockedTokenFactoryModule'; +import * as moduleWrapperFactoryModule from '../../testUtils/MockedModuleWrapperFactoryModule'; +import { mockFactories } from '../../testUtils/mockFactories'; +import { SecurityToken, TieredSto } from '../../entities'; +import * as securityTokenFactoryModule from '../../entities/factories/SecurityTokenFactory'; +import { Wallet } from '../../Wallet'; +import { ApproveErc20 } from '../../procedures'; + +const treasuryWallet = '0x1111111111111111111111111111111111111111'; +const currentWalletAddress = '0x2222222222222222222222222222222222222222'; +const polyTokenAddress = '0x8888888888888888888888888888888888888888'; +const erc20TokenAddress = '0x9999999999999999999999999999999999999999'; + +const tieredParams: InvestInTieredStoProcedureArgs = { + symbol: 'TEST1', + stoAddress: '0x5555555555555555555555555555555555555555', + stableCoinAddress: '0x6666666666666666666666666666666666666666', + amount: new BigNumber(2), + currency: Currency.StableCoin, + minTokens: new BigNumber(1), + beneficiary: '0x3333333333333333333333333333333333333333', +}; + +const tieredStoObject = { + isFinalized: false, + isPaused: false, + startDate: new Date(2010, 1), + beneficialInvestmentsAllowed: true, + fundraiseCurrencies: [Currency.StableCoin], +}; + +describe('InvestInTieredSto', () => { + let target: InvestInTieredSto; + let contextMock: MockManager; + let wrappersMock: MockManager; + let securityTokenFactoryMock: MockManager; + let tokenFactoryMock: MockManager; + let moduleWrapperFactoryMock: MockManager< + moduleWrapperFactoryModule.MockedModuleWrapperFactoryModule + >; + let polyTokenMock: MockManager; + let erc20TokenMock: MockManager; + let tieredStoMock: MockManager; + + // Mock factories + let tieredStoFactoryMock: MockManager; + + let securityTokenMock: MockManager; + + let factoryMockSetup: Factories; + let securityTokenId: string; + let tieredStoId: string; + + beforeEach(() => { + // Mock the context, wrappers, and tokenFactory to test InvestInTieredSto + contextMock = ImportMock.mockClass(contextModule, 'Context'); + wrappersMock = ImportMock.mockClass(wrappersModule, 'PolymathBase'); + tokenFactoryMock = ImportMock.mockClass(tokenFactoryModule, 'MockedTokenFactoryModule'); + moduleWrapperFactoryMock = ImportMock.mockClass( + moduleWrapperFactoryModule, + 'MockedModuleWrapperFactoryModule' + ); + + contextMock.set('contractWrappers', wrappersMock.getMockInstance()); + wrappersMock.set('tokenFactory', tokenFactoryMock.getMockInstance()); + wrappersMock.set('moduleFactory', moduleWrapperFactoryMock.getMockInstance()); + + securityTokenMock = ImportMock.mockClass(contractWrappersModule, 'SecurityToken_3_0_0'); + + securityTokenFactoryMock = ImportMock.mockClass( + securityTokenFactoryModule, + 'SecurityTokenFactory' + ); + + tokenFactoryMock.mock( + 'getSecurityTokenInstanceFromTicker', + securityTokenMock.getMockInstance() + ); + + polyTokenMock = ImportMock.mockClass(contractWrappersModule, 'PolyToken'); + polyTokenMock.mock('balanceOf', new BigNumber(10)); + polyTokenMock.mock('address', polyTokenAddress); + polyTokenMock.mock('allowance', new BigNumber(10)); + + erc20TokenMock = ImportMock.mockClass(contractWrappersModule, 'ERC20'); + erc20TokenMock.mock('balanceOf', Promise.resolve(new BigNumber(10))); + erc20TokenMock.mock('address', Promise.resolve(erc20TokenAddress)); + erc20TokenMock.mock('allowance', Promise.resolve(new BigNumber(0))); + + wrappersMock.mock('getERC20TokenWrapper', erc20TokenMock.getMockInstance()); + + wrappersMock.mock('isTestnet', false); + wrappersMock.set('polyToken', polyTokenMock.getMockInstance()); + + tieredStoFactoryMock = ImportMock.mockClass(tieredStoFactoryModule, 'TieredStoFactory'); + + factoryMockSetup = mockFactories(); + factoryMockSetup.tieredStoFactory = tieredStoFactoryMock.getMockInstance(); + factoryMockSetup.securityTokenFactory = securityTokenFactoryMock.getMockInstance(); + contextMock.set('factories', factoryMockSetup); + + tieredStoMock = ImportMock.mockClass(contractWrappersModule, 'CappedSTO_3_1_0'); + + securityTokenId = SecurityToken.generateId({ symbol: tieredParams.symbol }); + tieredStoId = TieredSto.generateId({ + securityTokenId, + stoType: StoType.Tiered, + address: tieredParams.stoAddress, + }); + + moduleWrapperFactoryMock.mock('getModuleInstance', tieredStoMock.getMockInstance()); + contextMock.set( + 'currentWallet', + new Wallet({ address: () => Promise.resolve(currentWalletAddress) }) + ); + + tieredStoMock.mock('isFinalized', Promise.resolve(false)); + + tieredStoFactoryMock.mock('fetch', tieredStoObject); + + wrappersMock.mock('getTreasuryWallet', Promise.resolve(treasuryWallet)); + + // Instantiate InvestInTieredSto with a tiered sto + target = new InvestInTieredSto(tieredParams, contextMock.getMockInstance()); + }); + + afterEach(() => { + restore(); + }); + + describe('Types', () => { + test('should extend procedure and have InvestInTieredSto type', async () => { + expect(target instanceof Procedure).toBe(true); + expect(target.type).toBe(ProcedureType.InvestInTieredSto); + }); + }); + + describe('InvestInTieredSto', () => { + test('should add a transaction to the queue to invest in a tiered sto with stablecoin', async () => { + const addTransactionSpy = spy(target, 'addTransaction'); + const addProcedureSpy = spy(target, 'addProcedure'); + tieredStoMock.mock('buyWithUSDRateLimited', Promise.resolve('BuyWithUSDRateLimited')); + + securityTokenMock.mock('balanceOf', Promise.resolve(new BigNumber(10))); + + // Real call + await target.prepareTransactions(); + + // Verifications + expect( + addTransactionSpy + .getCall(0) + .calledWith(tieredStoMock.getMockInstance().buyWithUSDRateLimited) + ).toEqual(true); + expect(addTransactionSpy.getCall(0).lastArg.tag).toEqual( + PolyTransactionTag.BuyWithScRateLimited + ); + expect(addTransactionSpy.callCount).toEqual(1); + expect(addProcedureSpy.getCall(0).calledWithExactly(ApproveErc20)).toEqual(true); + expect(addProcedureSpy.callCount).toEqual(1); + }); + + test('should add a transaction to the queue to invest in a tiered sto with stablecoin without minimum tokens or beneficiary specified', async () => { + target = new InvestInTieredSto( + { + ...tieredParams, + minTokens: undefined, + beneficiary: undefined, + }, + contextMock.getMockInstance() + ); + const addTransactionSpy = spy(target, 'addTransaction'); + const addProcedureSpy = spy(target, 'addProcedure'); + tieredStoMock.mock('buyWithUSDRateLimited', Promise.resolve('BuyWithUSDRateLimited')); + + securityTokenMock.mock('balanceOf', Promise.resolve(new BigNumber(10))); + + // Real call + await target.prepareTransactions(); + + // Verifications + expect( + addTransactionSpy + .getCall(0) + .calledWith(tieredStoMock.getMockInstance().buyWithUSDRateLimited) + ).toEqual(true); + expect(addTransactionSpy.getCall(0).lastArg.tag).toEqual( + PolyTransactionTag.BuyWithScRateLimited + ); + expect(addTransactionSpy.callCount).toEqual(1); + expect(addProcedureSpy.getCall(0).calledWithExactly(ApproveErc20)).toEqual(true); + expect(addProcedureSpy.callCount).toEqual(1); + }); + + test('should throw an error if the arguments indicate the currency is stable coin, but stable coin is not listed as a fundraise currency type', async () => { + tieredStoFactoryMock.mock('fetch', { + ...tieredStoObject, + fundraiseCurrencies: [FundRaiseType.ETH], + }); + await expect(target.prepareTransactions()).rejects.toThrow( + new PolymathError({ + code: ErrorCode.ProcedureValidationError, + message: `STO ${ + tieredParams.stoAddress + } doesn't support investments in the selected currency`, + }) + ); + }); + + test('should add a transaction to the queue to invest in a tiered sto with POLY', async () => { + target = new InvestInTieredSto( + { + ...tieredParams, + currency: Currency.POLY, + stableCoinAddress: undefined, + }, + contextMock.getMockInstance() + ); + tieredStoFactoryMock.mock('fetch', { + ...tieredStoObject, + fundraiseCurrencies: [FundRaiseType.POLY], + }); + const addTransactionSpy = spy(target, 'addTransaction'); + tieredStoMock.mock('buyWithPOLYRateLimited', Promise.resolve('BuyWithPOLYRateLimited')); + + // Real call + await target.prepareTransactions(); + + // Verifications + expect( + addTransactionSpy + .getCall(0) + .calledWith(tieredStoMock.getMockInstance().buyWithPOLYRateLimited) + ).toEqual(true); + expect(addTransactionSpy.getCall(0).lastArg.tag).toEqual( + PolyTransactionTag.BuyWithPolyRateLimited + ); + expect(addTransactionSpy.callCount).toEqual(1); + }); + + test('should throw an error if the arguments indicate the currency is POLY, but POLY is not listed as a fundraise currency type', async () => { + target = new InvestInTieredSto( + { + ...tieredParams, + currency: Currency.POLY, + stableCoinAddress: undefined, + }, + contextMock.getMockInstance() + ); + + await expect(target.prepareTransactions()).rejects.toThrow( + new PolymathError({ + code: ErrorCode.ProcedureValidationError, + message: `STO ${ + tieredParams.stoAddress + } doesn't support investments in the selected currency`, + }) + ); + }); + + test('should add a transaction to the queue to invest in a tiered sto with ETH', async () => { + target = new InvestInTieredSto( + { + ...tieredParams, + currency: Currency.ETH, + stableCoinAddress: undefined, + }, + contextMock.getMockInstance() + ); + tieredStoFactoryMock.mock('fetch', { + ...tieredStoObject, + fundraiseCurrencies: [FundRaiseType.ETH], + }); + const addTransactionSpy = spy(target, 'addTransaction'); + tieredStoMock.mock('buyWithETHRateLimited', Promise.resolve('BuyWithETHRateLimited')); + + // Real call + await target.prepareTransactions(); + + // Verifications + expect( + addTransactionSpy + .getCall(0) + .calledWith(tieredStoMock.getMockInstance().buyWithETHRateLimited) + ).toEqual(true); + expect(addTransactionSpy.getCall(0).lastArg.tag).toEqual( + PolyTransactionTag.BuyWithEthRateLimited + ); + expect(addTransactionSpy.callCount).toEqual(1); + }); + + test('should throw an error if the arguments indicate the currency is ETH, but ETH is not listed as a fundraise currency type', async () => { + target = new InvestInTieredSto( + { + ...tieredParams, + currency: Currency.ETH, + stableCoinAddress: undefined, + }, + contextMock.getMockInstance() + ); + + await expect(target.prepareTransactions()).rejects.toThrow( + new PolymathError({ + code: ErrorCode.ProcedureValidationError, + message: `STO ${ + tieredParams.stoAddress + } doesn't support investments in the selected currency`, + }) + ); + }); + + test('should throw an error if the tiered sto has not yet been launched or is archived', async () => { + moduleWrapperFactoryMock.mock('getModuleInstance', Promise.resolve(undefined)); + + await expect(target.prepareTransactions()).rejects.toThrow( + new PolymathError({ + code: ErrorCode.InvalidAddress, + message: `STO ${tieredParams.stoAddress} is either archived or hasn't been launched`, + }) + ); + }); + + test('should throw if there is an invalid sto address', async () => { + target = new InvestInTieredSto( + { + ...tieredParams, + stoAddress: 'invalid', + }, + contextMock.getMockInstance() + ); + await expect(target.prepareTransactions()).rejects.toThrow( + new PolymathError({ + code: ErrorCode.InvalidAddress, + message: `Invalid STO address invalid`, + }) + ); + }); + + test('should throw if there is no valid security token supplied', async () => { + tokenFactoryMock + .mock('getSecurityTokenInstanceFromTicker') + .withArgs(tieredParams.symbol) + .throws(); + + await expect(target.prepareTransactions()).rejects.toThrow( + new PolymathError({ + code: ErrorCode.ProcedureValidationError, + message: `There is no Security Token with symbol ${tieredParams.symbol}`, + }) + ); + }); + + test('should throw an error if the sto has already been finalized', async () => { + tieredStoFactoryMock.mock('fetch', { + ...tieredStoObject, + isFinalized: true, + }); + + await expect(target.prepareTransactions()).rejects.toThrow( + new PolymathError({ + code: ErrorCode.ProcedureValidationError, + message: `STO ${tieredParams.stoAddress} has already been finalized`, + }) + ); + }); + + test('should throw an error if the sto start date is in the future', async () => { + tieredStoFactoryMock.mock('fetch', { + ...tieredStoObject, + startDate: new Date(2040, 0), + }); + + await expect(target.prepareTransactions()).rejects.toThrow( + new PolymathError({ + code: ErrorCode.ProcedureValidationError, + message: `Cannot invest in STO ${tieredParams.stoAddress} because it hasn't started yet`, + }) + ); + }); + + test('should throw an error if beneficial investments are not allowed and the parameters include a beneficiary address', async () => { + tieredStoFactoryMock.mock('fetch', { + ...tieredStoObject, + beneficialInvestmentsAllowed: false, + }); + + await expect(target.prepareTransactions()).rejects.toThrow( + new PolymathError({ + code: ErrorCode.ProcedureValidationError, + message: `Cannot invest on behalf of ${ + tieredParams.beneficiary + } because this STO doesn't allow beneficial investments`, + }) + ); + }); + + test('should throw an error if the sto is paused', async () => { + tieredStoFactoryMock.mock('fetch', { + ...tieredStoObject, + isPaused: true, + }); + + await expect(target.prepareTransactions()).rejects.toThrow( + new PolymathError({ + code: ErrorCode.ProcedureValidationError, + message: `STO ${tieredParams.stoAddress} is paused`, + }) + ); + }); + + test('should successfully refresh the tiered sto factory with its resolver method', async () => { + const refreshStub = tieredStoFactoryMock.mock('refresh', Promise.resolve()); + await investInTieredStoModule.createRefreshTieredStoFactoryResolver( + factoryMockSetup, + tieredStoId + )(); + + expect(refreshStub.getCall(0).calledWithExactly(tieredStoId)).toEqual(true); + expect(refreshStub.callCount).toEqual(1); + }); + + test('should successfully refresh the security token factory with its resolver method', async () => { + const refreshStub = securityTokenFactoryMock.mock('refresh', Promise.resolve()); + await investInTieredStoModule.createRefreshSecurityTokenFactoryResolver( + factoryMockSetup, + securityTokenId + )(); + + expect(refreshStub.getCall(0).calledWithExactly(securityTokenId)).toEqual(true); + expect(refreshStub.callCount).toEqual(1); + }); + }); +}); From 994c51d8a049dbaec5d9f543f89ceb7a505039b6 Mon Sep 17 00:00:00 2001 From: Victor Wiebe Date: Fri, 20 Dec 2019 14:15:29 -0800 Subject: [PATCH 15/33] fix: remove unnecessary code --- src/procedures/__tests__/InvestInTieredSto.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/procedures/__tests__/InvestInTieredSto.ts b/src/procedures/__tests__/InvestInTieredSto.ts index 37b7878..def2ee8 100644 --- a/src/procedures/__tests__/InvestInTieredSto.ts +++ b/src/procedures/__tests__/InvestInTieredSto.ts @@ -1,7 +1,7 @@ import { ImportMock, MockManager } from 'ts-mock-imports'; import { restore, spy } from 'sinon'; import * as contractWrappersModule from '@polymathnetwork/contract-wrappers'; -import { BigNumber, FundRaiseType, TransferStatusCode } from '@polymathnetwork/contract-wrappers'; +import { BigNumber, FundRaiseType } from '@polymathnetwork/contract-wrappers'; import * as investInTieredStoModule from '../InvestInTieredSto'; import { InvestInTieredSto } from '../InvestInTieredSto'; import { Procedure } from '../Procedure'; From ac631260a24e070778b48ff5114de4b1ea84b202 Mon Sep 17 00:00:00 2001 From: Victor Wiebe Date: Mon, 23 Dec 2019 06:24:28 -0800 Subject: [PATCH 16/33] fix: small fixes before review --- src/procedures/__tests__/FinalizeSto.ts | 2 +- src/procedures/__tests__/InvestInTieredSto.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/procedures/__tests__/FinalizeSto.ts b/src/procedures/__tests__/FinalizeSto.ts index a3330bb..8b06110 100644 --- a/src/procedures/__tests__/FinalizeSto.ts +++ b/src/procedures/__tests__/FinalizeSto.ts @@ -171,7 +171,7 @@ describe('FinalizeSto', () => { // Real call await target.prepareTransactions(); - // Verifications\ + // Verifications expect( addTransactionSpy.getCall(0).calledWith(tieredStoMock.getMockInstance().finalize) ).toEqual(true); diff --git a/src/procedures/__tests__/InvestInTieredSto.ts b/src/procedures/__tests__/InvestInTieredSto.ts index def2ee8..2a0d72b 100644 --- a/src/procedures/__tests__/InvestInTieredSto.ts +++ b/src/procedures/__tests__/InvestInTieredSto.ts @@ -105,7 +105,7 @@ describe('InvestInTieredSto', () => { erc20TokenMock = ImportMock.mockClass(contractWrappersModule, 'ERC20'); erc20TokenMock.mock('balanceOf', Promise.resolve(new BigNumber(10))); erc20TokenMock.mock('address', Promise.resolve(erc20TokenAddress)); - erc20TokenMock.mock('allowance', Promise.resolve(new BigNumber(0))); + erc20TokenMock.mock('allowance', Promise.resolve(new BigNumber(10))); wrappersMock.mock('getERC20TokenWrapper', erc20TokenMock.getMockInstance()); From 0b60616cc9a161998d3daff83b42157e94addf5a Mon Sep 17 00:00:00 2001 From: Shuffledex Date: Mon, 23 Dec 2019 14:21:56 -0300 Subject: [PATCH 17/33] test: pullDividendPayment test suite cover at 100% --- .../__tests__/PullDividendPayment.ts | 228 ++++++++++++++++++ 1 file changed, 228 insertions(+) create mode 100644 src/procedures/__tests__/PullDividendPayment.ts diff --git a/src/procedures/__tests__/PullDividendPayment.ts b/src/procedures/__tests__/PullDividendPayment.ts new file mode 100644 index 0000000..977a55e --- /dev/null +++ b/src/procedures/__tests__/PullDividendPayment.ts @@ -0,0 +1,228 @@ +import { ImportMock, MockManager } from 'ts-mock-imports'; +import { spy, restore } from 'sinon'; +import * as contractWrappersModule from '@polymathnetwork/contract-wrappers'; +import * as contextModule from '../../Context'; +import * as wrappersModule from '../../PolymathBase'; +import * as tokenFactoryModule from '../../testUtils/MockedTokenFactoryModule'; +import * as dividendFactoryModule from '../../entities/factories/DividendDistributionFactory'; +import * as pullDividendPaymentModule from '../../procedures/PullDividendPayment'; +import { PullDividendPayment } from '../../procedures/PullDividendPayment'; +import { Procedure } from '../../procedures/Procedure'; +import { ProcedureType, ErrorCode, PolyTransactionTag } from '../../types'; +import { PolymathError } from '../../PolymathError'; +import { mockFactories } from '../../testUtils/mockFactories'; +import { Factories } from '../../Context'; +import { Wallet } from '../../Wallet'; +import { SecurityToken, DividendDistribution } from '../../entities'; + +const params = { + symbol: 'TEST', + dividendIndex: 0, +}; + +describe('PullDividendPayment', () => { + let target: PullDividendPayment; + let contextMock: MockManager; + let wrappersMock: MockManager; + let tokenFactoryMock: MockManager; + let securityTokenMock: MockManager; + let erc20DividendsMock: MockManager; + let dividendFactoryMock: MockManager; + let factoriesMockedSetup: Factories; + + beforeEach(() => { + // Mock the context, wrappers, tokenFactory and securityToken to test PullDividendPayment + contextMock = ImportMock.mockClass(contextModule, 'Context'); + wrappersMock = ImportMock.mockClass(wrappersModule, 'PolymathBase'); + tokenFactoryMock = ImportMock.mockClass(tokenFactoryModule, 'MockedTokenFactoryModule'); + securityTokenMock = ImportMock.mockClass(contractWrappersModule, 'SecurityToken_3_0_0'); + erc20DividendsMock = ImportMock.mockClass( + contractWrappersModule, + 'ERC20DividendCheckpoint_3_0_0' + ); + tokenFactoryMock.mock( + 'getSecurityTokenInstanceFromTicker', + securityTokenMock.getMockInstance() + ); + contextMock.set('contractWrappers', wrappersMock.getMockInstance()); + wrappersMock.set('tokenFactory', tokenFactoryMock.getMockInstance()); + dividendFactoryMock = ImportMock.mockClass( + dividendFactoryModule, + 'DividendDistributionFactory' + ); + factoriesMockedSetup = mockFactories(); + factoriesMockedSetup.dividendDistributionFactory = dividendFactoryMock.getMockInstance(); + contextMock.set('factories', factoriesMockedSetup); + contextMock.set('currentWallet', new Wallet({ address: () => Promise.resolve('0x01') })); + target = new PullDividendPayment(params, contextMock.getMockInstance()); + }); + + afterEach(() => { + restore(); + }); + + describe('Types', () => { + test('should extend procedure and have PullDividendPayment type', async () => { + expect(target instanceof Procedure).toBe(true); + expect(target.type).toBe(ProcedureType.PullDividendPayment); + }); + }); + + describe('PullDividendPayment', () => { + test('should throw if there is no valid security token being provided', async () => { + tokenFactoryMock + .mock('getSecurityTokenInstanceFromTicker') + .withArgs(params.symbol) + .throws(); + + await expect(target.prepareTransactions()).rejects.toThrow( + new PolymathError({ + code: ErrorCode.ProcedureValidationError, + message: `There is no Security Token with symbol ${params.symbol}`, + }) + ); + }); + + test('should throw if an Erc20 dividend module is not attached', async () => { + wrappersMock.mock('getAttachedModules', Promise.resolve([])); + + // Real call + await expect(target.prepareTransactions()).rejects.toThrowError( + new PolymathError({ + code: ErrorCode.ProcedureValidationError, + message: "The Dividends Feature hasn't been enabled", + }) + ); + }); + + test('should throw if the owner address is not a shareholder', async () => { + contextMock.set('currentWallet', new Wallet({ address: () => Promise.resolve('0x04') })); + + wrappersMock.mock( + 'getAttachedModules', + Promise.resolve([erc20DividendsMock.getMockInstance()]) + ); + + wrappersMock.mock( + 'getDividend', + Promise.resolve({ + shareholders: [{ address: '0x01', paymentReceived: false, excluded: false }], + }) + ); + + // Real call + await expect(target.prepareTransactions()).rejects.toThrowError( + new PolymathError({ + code: ErrorCode.ProcedureValidationError, + message: + 'Current wallet 0x04 cannot receive dividend payments. Reason: not a shareholder', + }) + ); + }); + + test('should throw if a shareholder already received payment', async () => { + wrappersMock.mock( + 'getAttachedModules', + Promise.resolve([erc20DividendsMock.getMockInstance()]) + ); + + wrappersMock.mock( + 'getDividend', + Promise.resolve({ + shareholders: [{ address: '0x01', paymentReceived: true, excluded: false }], + }) + ); + + // Real call + await expect(target.prepareTransactions()).rejects.toThrowError( + new PolymathError({ + code: ErrorCode.ProcedureValidationError, + message: + 'Current wallet 0x01 cannot receive dividend payments. Reason: already received payment', + }) + ); + }); + + test('should throw if a shareholder is in the exclusion list', async () => { + wrappersMock.mock( + 'getAttachedModules', + Promise.resolve([erc20DividendsMock.getMockInstance()]) + ); + + wrappersMock.mock( + 'getDividend', + Promise.resolve({ + shareholders: [{ address: '0x01', paymentReceived: false, excluded: true }], + }) + ); + + // Real call + await expect(target.prepareTransactions()).rejects.toThrowError( + new PolymathError({ + code: ErrorCode.ProcedureValidationError, + message: + 'Current wallet 0x01 cannot receive dividend payments. Reason: address belongs to exclusion list', + }) + ); + }); + + test('should add a transaction to pull a dividend payment', async () => { + wrappersMock.mock( + 'getAttachedModules', + Promise.resolve([erc20DividendsMock.getMockInstance()]) + ); + + wrappersMock.mock( + 'getDividend', + Promise.resolve({ + shareholders: [ + { address: '0x01', paymentReceived: false, excluded: false }, + { address: '0x02', paymentReceived: false, excluded: false }, + { address: '0x03', paymentReceived: true, excluded: false }, + ], + }) + ); + + erc20DividendsMock.mock('pullDividendPayment', Promise.resolve('PullDividendPayment')); + + const addTransactionSpy = spy(target, 'addTransaction'); + + // Real call + await target.prepareTransactions(); + + // Verifications + expect( + addTransactionSpy + .getCall(0) + .calledWith(erc20DividendsMock.getMockInstance().pullDividendPayment) + ).toEqual(true); + expect(addTransactionSpy.getCall(0).lastArg.tag).toEqual( + PolyTransactionTag.PullDividendPayment + ); + expect(addTransactionSpy.callCount).toEqual(1); + }); + + test('should refresh the dividend distribution for which payments were pulled', async () => { + const refreshStub = dividendFactoryMock.mock('refresh', Promise.resolve(undefined)); + + const resolverValue = await pullDividendPaymentModule.createPullDividendPaymentResolver( + factoriesMockedSetup, + params.symbol, + 0 + )(); + + expect( + refreshStub.getCall(0).calledWithExactly( + DividendDistribution.generateId({ + securityTokenId: SecurityToken.generateId({ + symbol: params.symbol, + }), + index: 0, + }) + ) + ).toEqual(true); + expect(resolverValue).toEqual(undefined); + expect(refreshStub.callCount).toEqual(1); + }); + }); +}); From 0944daea6e951ca222bf8a38008581f1f1838ef9 Mon Sep 17 00:00:00 2001 From: Victor Wiebe Date: Mon, 23 Dec 2019 10:19:27 -0800 Subject: [PATCH 18/33] refactor: extract resolver out --- src/procedures/ModifyBeneficialInvestments.ts | 56 +++++++++++-------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/src/procedures/ModifyBeneficialInvestments.ts b/src/procedures/ModifyBeneficialInvestments.ts index 819d40c..07606a6 100644 --- a/src/procedures/ModifyBeneficialInvestments.ts +++ b/src/procedures/ModifyBeneficialInvestments.ts @@ -10,6 +10,37 @@ import { import { PolymathError } from '../PolymathError'; import { isValidAddress } from '../utils'; import { SecurityToken, SimpleSto, TieredSto } from '../entities'; +import { Factories } from '../Context'; + +export const createModifyBeneficialInvestmentsResolver = ( + factories: Factories, + symbol: string, + stoType: StoType, + stoAddress: string +) => async () => { + const securityTokenId = SecurityToken.generateId({ symbol }); + + switch (stoType) { + case StoType.Simple: { + return factories.simpleStoFactory.refresh( + SimpleSto.generateId({ + securityTokenId, + stoType, + address: stoAddress, + }) + ); + } + case StoType.Tiered: { + return factories.tieredStoFactory.refresh( + TieredSto.generateId({ + securityTokenId, + stoType, + address: stoAddress, + }) + ); + } + } +}; export class ModifyBeneficialInvestments extends Procedure< ModifyBeneficialInvestmentsProcedureArgs @@ -83,30 +114,7 @@ export class ModifyBeneficialInvestments extends Procedure< await this.addTransaction(stoModule.changeAllowBeneficialInvestments, { tag: PolyTransactionTag.ChangeAllowBeneficialInvestments, resolvers: [ - async () => { - const securityTokenId = SecurityToken.generateId({ symbol }); - - switch (stoType) { - case StoType.Simple: { - return factories.simpleStoFactory.refresh( - SimpleSto.generateId({ - securityTokenId, - stoType, - address: stoAddress, - }) - ); - } - case StoType.Tiered: { - return factories.tieredStoFactory.refresh( - TieredSto.generateId({ - securityTokenId, - stoType, - address: stoAddress, - }) - ); - } - } - }, + createModifyBeneficialInvestmentsResolver(factories, symbol, stoType, stoAddress), ], })({ allowBeneficialInvestments }); } From 161f9b0de0c67894a675c12eeb82fe4ee48de2cb Mon Sep 17 00:00:00 2001 From: Victor Wiebe Date: Mon, 23 Dec 2019 10:47:30 -0800 Subject: [PATCH 19/33] refactor: rename modifyBeneficialInvesments and modifyPreIssuing --- src/entities/Sto.ts | 12 ++++++------ ...tments.ts => ToggleAllowBeneficialInvestments.ts} | 12 ++++++------ ...{ModifyPreIssuing.ts => ToggleAllowPreIssuing.ts} | 6 +++--- src/procedures/index.ts | 4 ++-- src/types/index.ts | 12 ++++++------ 5 files changed, 23 insertions(+), 23 deletions(-) rename src/procedures/{ModifyBeneficialInvestments.ts => ToggleAllowBeneficialInvestments.ts} (88%) rename src/procedures/{ModifyPreIssuing.ts => ToggleAllowPreIssuing.ts} (94%) diff --git a/src/entities/Sto.ts b/src/entities/Sto.ts index 71e8912..231ce69 100644 --- a/src/entities/Sto.ts +++ b/src/entities/Sto.ts @@ -8,8 +8,8 @@ import { TogglePauseSto, AssignStoRole, FinalizeSto, - ModifyBeneficialInvestments, - ModifyPreIssuing, + ToggleAllowBeneficialInvestments, + ToggleAllowPreIssuing, } from '../procedures'; export interface UniqueIdentifiers { @@ -183,7 +183,7 @@ export abstract class Sto

extends Entity

{ public allowPreIssuing = async () => { const { address: stoAddress, stoType, securityTokenSymbol: symbol } = this; - const procedure = new ModifyPreIssuing( + const procedure = new ToggleAllowPreIssuing( { stoAddress, stoType, symbol, allowPreIssuing: true }, this.context ); @@ -198,7 +198,7 @@ export abstract class Sto

extends Entity

{ public disallowPreIssuing = async () => { const { address: stoAddress, stoType, securityTokenSymbol: symbol } = this; - const procedure = new ModifyPreIssuing( + const procedure = new ToggleAllowPreIssuing( { stoAddress, stoType, symbol, allowPreIssuing: false }, this.context ); @@ -212,7 +212,7 @@ export abstract class Sto

extends Entity

{ public allowBeneficialInvestments = async () => { const { address: stoAddress, stoType, securityTokenSymbol: symbol } = this; - const procedure = new ModifyBeneficialInvestments( + const procedure = new ToggleAllowBeneficialInvestments( { stoAddress, stoType, symbol, allowBeneficialInvestments: true }, this.context ); @@ -226,7 +226,7 @@ export abstract class Sto

extends Entity

{ public disallowBeneficialInvestments = async () => { const { address: stoAddress, stoType, securityTokenSymbol: symbol } = this; - const procedure = new ModifyBeneficialInvestments( + const procedure = new ToggleAllowBeneficialInvestments( { stoAddress, stoType, symbol, allowBeneficialInvestments: false }, this.context ); diff --git a/src/procedures/ModifyBeneficialInvestments.ts b/src/procedures/ToggleAllowBeneficialInvestments.ts similarity index 88% rename from src/procedures/ModifyBeneficialInvestments.ts rename to src/procedures/ToggleAllowBeneficialInvestments.ts index 07606a6..805afe3 100644 --- a/src/procedures/ModifyBeneficialInvestments.ts +++ b/src/procedures/ToggleAllowBeneficialInvestments.ts @@ -3,7 +3,7 @@ import { Procedure } from './Procedure'; import { ProcedureType, PolyTransactionTag, - ModifyBeneficialInvestmentsProcedureArgs, + ToggleAllowBeneficialInvestmentsProcedureArgs, ErrorCode, StoType, } from '../types'; @@ -12,7 +12,7 @@ import { isValidAddress } from '../utils'; import { SecurityToken, SimpleSto, TieredSto } from '../entities'; import { Factories } from '../Context'; -export const createModifyBeneficialInvestmentsResolver = ( +export const createToggleAllowBeneficialInvestmentsResolver = ( factories: Factories, symbol: string, stoType: StoType, @@ -42,10 +42,10 @@ export const createModifyBeneficialInvestmentsResolver = ( } }; -export class ModifyBeneficialInvestments extends Procedure< - ModifyBeneficialInvestmentsProcedureArgs +export class ToggleAllowBeneficialInvestments extends Procedure< + ToggleAllowBeneficialInvestmentsProcedureArgs > { - public type = ProcedureType.ModifyBeneficialInvestments; + public type = ProcedureType.ToggleAllowBeneficialInvestments; public async prepareTransactions() { const { stoAddress, stoType, symbol, allowBeneficialInvestments } = this.args; @@ -114,7 +114,7 @@ export class ModifyBeneficialInvestments extends Procedure< await this.addTransaction(stoModule.changeAllowBeneficialInvestments, { tag: PolyTransactionTag.ChangeAllowBeneficialInvestments, resolvers: [ - createModifyBeneficialInvestmentsResolver(factories, symbol, stoType, stoAddress), + createToggleAllowBeneficialInvestmentsResolver(factories, symbol, stoType, stoAddress), ], })({ allowBeneficialInvestments }); } diff --git a/src/procedures/ModifyPreIssuing.ts b/src/procedures/ToggleAllowPreIssuing.ts similarity index 94% rename from src/procedures/ModifyPreIssuing.ts rename to src/procedures/ToggleAllowPreIssuing.ts index 3dfa0d5..c42aed4 100644 --- a/src/procedures/ModifyPreIssuing.ts +++ b/src/procedures/ToggleAllowPreIssuing.ts @@ -7,7 +7,7 @@ import { Procedure } from './Procedure'; import { ProcedureType, PolyTransactionTag, - ModifyPreIssuingProcedureArgs, + ToggleAllowPreIssuingProcedureArgs, ErrorCode, StoType, } from '../types'; @@ -15,8 +15,8 @@ import { PolymathError } from '../PolymathError'; import { isValidAddress } from '../utils'; import { SecurityToken, SimpleSto, TieredSto } from '../entities'; -export class ModifyPreIssuing extends Procedure { - public type = ProcedureType.ModifyPreIssuing; +export class ToggleAllowPreIssuing extends Procedure { + public type = ProcedureType.ToggleAllowPreIssuing; public async prepareTransactions() { const { stoAddress, stoType, symbol, allowPreIssuing } = this.args; diff --git a/src/procedures/index.ts b/src/procedures/index.ts index d337a51..3035a59 100644 --- a/src/procedures/index.ts +++ b/src/procedures/index.ts @@ -30,7 +30,7 @@ export { ModifyShareholderData } from './ModifyShareholderData'; export { RevokeKyc } from './RevokeKyc'; export { IssueTokens } from './IssueTokens'; export { FinalizeSto } from './FinalizeSto'; -export { ModifyBeneficialInvestments } from './ModifyBeneficialInvestments'; +export { ToggleAllowBeneficialInvestments } from './ToggleAllowBeneficialInvestments'; export { ModifyTieredStoData } from './ModifyTieredStoData'; export { InvestInTieredSto } from './InvestInTieredSto'; export { InvestInSimpleSto } from './InvestInSimpleSto'; @@ -42,5 +42,5 @@ export { ModifyPercentageExemptions } from './ModifyPercentageExemptions'; export { TransferSecurityTokens } from './TransferSecurityTokens'; export { ToggleFreezeTransfers } from './ToggleFreezeTransfers'; export { ModifyDividendsDefaultExclusionList } from './ModifyDividendsDefaultExclusionList'; -export { ModifyPreIssuing } from './ModifyPreIssuing'; +export { ToggleAllowPreIssuing } from './ToggleAllowPreIssuing'; export { TransferReservationOwnership } from './TransferReservationOwnership'; diff --git a/src/types/index.ts b/src/types/index.ts index fd17c86..5195d05 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -153,8 +153,8 @@ export enum ProcedureType { ModifyShareholderData = 'ModifyShareholderData', RevokeKyc = 'RevokeKyc', IssueTokens = 'IssueTokens', - ModifyPreIssuing = 'ModifyPreIssuing', - ModifyBeneficialInvestments = 'ModifyBeneificialInvestments', + ToggleAllowPreIssuing = 'ToggleAllowPreIssuing', + ToggleAllowBeneficialInvestments = 'ToggleAllowBeneficialInvestments', ModifyTieredStoData = 'ModifyTieredStoData', InvestInTieredSto = 'InvestInTieredSto', InvestInSimpleSto = 'InvestInSimpleSto', @@ -347,14 +347,14 @@ export interface IssueTokensProcedureArgs { issuanceData: IssuanceDataEntry[]; } -export interface ModifyPreIssuingProcedureArgs { +export interface ToggleAllowPreIssuingProcedureArgs { symbol: string; stoAddress: string; stoType: StoType; allowPreIssuing: boolean; } -export interface ModifyBeneficialInvestmentsProcedureArgs { +export interface ToggleAllowBeneficialInvestmentsProcedureArgs { symbol: string; stoAddress: string; stoType: StoType; @@ -652,10 +652,10 @@ export interface ProcedureArguments { [ProcedureType.ModifyShareholderData]: ModifyShareholderDataProcedureArgs; [ProcedureType.RevokeKyc]: RevokeKycProcedureArgs; [ProcedureType.IssueTokens]: IssueTokensProcedureArgs; - [ProcedureType.ModifyPreIssuing]: ModifyPreIssuingProcedureArgs; + [ProcedureType.ToggleAllowPreIssuing]: ToggleAllowPreIssuingProcedureArgs; [ProcedureType.DisableFeature]: DisableFeatureProcedureArgs; [ProcedureType.FinalizeSto]: FinalizeStoProcedureArgs; - [ProcedureType.ModifyBeneficialInvestments]: ModifyBeneficialInvestmentsProcedureArgs; + [ProcedureType.ToggleAllowBeneficialInvestments]: ToggleAllowBeneficialInvestmentsProcedureArgs; [ProcedureType.ModifyTieredStoData]: ModifyTieredStoDataProcedureArgs; [ProcedureType.InvestInTieredSto]: InvestInTieredStoProcedureArgs; [ProcedureType.InvestInSimpleSto]: InvestInSimpleStoProcedureArgs; From d13c05e6a3e8de54c294062334343943f8440b40 Mon Sep 17 00:00:00 2001 From: Victor Wiebe Date: Mon, 23 Dec 2019 17:15:43 -0800 Subject: [PATCH 20/33] test: toggle allow pre issuing and beneficial investments --- src/procedures/ToggleAllowPreIssuing.ts | 83 +++-- .../ToggleAllowBeneficialInvestments.ts | 262 ++++++++++++++ .../__tests__/ToggleAllowPreIssuing.ts | 337 ++++++++++++++++++ 3 files changed, 647 insertions(+), 35 deletions(-) create mode 100644 src/procedures/__tests__/ToggleAllowBeneficialInvestments.ts create mode 100644 src/procedures/__tests__/ToggleAllowPreIssuing.ts diff --git a/src/procedures/ToggleAllowPreIssuing.ts b/src/procedures/ToggleAllowPreIssuing.ts index c42aed4..e047682 100644 --- a/src/procedures/ToggleAllowPreIssuing.ts +++ b/src/procedures/ToggleAllowPreIssuing.ts @@ -13,7 +13,38 @@ import { } from '../types'; import { PolymathError } from '../PolymathError'; import { isValidAddress } from '../utils'; -import { SecurityToken, SimpleSto, TieredSto } from '../entities'; +import { Factories } from '~/Context'; +import { SecurityToken, SimpleSto, TieredSto } from '~/entities'; + +export const createToggleAllowPreIssuingResolver = ( + factories: Factories, + symbol: string, + stoType: StoType, + stoAddress: string +) => async () => { + const securityTokenId = SecurityToken.generateId({ symbol }); + + switch (stoType) { + case StoType.Simple: { + return factories.simpleStoFactory.refresh( + SimpleSto.generateId({ + securityTokenId, + stoType, + address: stoAddress, + }) + ); + } + case StoType.Tiered: { + return factories.tieredStoFactory.refresh( + TieredSto.generateId({ + securityTokenId, + stoType, + address: stoAddress, + }) + ); + } + } +}; export class ToggleAllowPreIssuing extends Procedure { public type = ProcedureType.ToggleAllowPreIssuing; @@ -39,6 +70,13 @@ export class ToggleAllowPreIssuing extends Procedure { - const securityTokenId = SecurityToken.generateId({ symbol }); - - switch (stoType) { - case StoType.Simple: { - return factories.simpleStoFactory.refresh( - SimpleSto.generateId({ - securityTokenId, - stoType, - address: stoAddress, - }) - ); - } - case StoType.Tiered: { - return factories.tieredStoFactory.refresh( - TieredSto.generateId({ - securityTokenId, - stoType, - address: stoAddress, - }) - ); - } - } - }, - ], + resolvers: [createToggleAllowPreIssuingResolver(factories, symbol, stoType, stoAddress)], } )({}); } diff --git a/src/procedures/__tests__/ToggleAllowBeneficialInvestments.ts b/src/procedures/__tests__/ToggleAllowBeneficialInvestments.ts new file mode 100644 index 0000000..ffcc661 --- /dev/null +++ b/src/procedures/__tests__/ToggleAllowBeneficialInvestments.ts @@ -0,0 +1,262 @@ +import { ImportMock, MockManager } from 'ts-mock-imports'; +import { restore, spy } from 'sinon'; +import * as contractWrappersModule from '@polymathnetwork/contract-wrappers'; +import { ToggleAllowBeneficialInvestments } from '../ToggleAllowBeneficialInvestments'; +import { Procedure } from '../Procedure'; +import { PolymathError } from '../../PolymathError'; +import { + ErrorCode, + ToggleAllowBeneficialInvestmentsProcedureArgs, + PolyTransactionTag, + ProcedureType, + StoType, +} from '../../types'; +import * as toggleAllowBeneficalInvestmentsModule from '../ToggleAllowBeneficialInvestments'; +import * as simpleStoFactoryModule from '../../entities/factories/SimpleStoFactory'; +import * as tieredStoFactoryModule from '../../entities/factories/TieredStoFactory'; +import * as contextModule from '../../Context'; +import * as wrappersModule from '../../PolymathBase'; +import * as moduleWrapperFactoryModule from '../../testUtils/MockedModuleWrapperFactoryModule'; +import { mockFactories } from '../../testUtils/mockFactories'; +import { Factories } from '../../Context'; +import { SimpleSto, SecurityToken, TieredSto } from '../../entities'; + +const tieredParams: ToggleAllowBeneficialInvestmentsProcedureArgs = { + symbol: 'TEST1', + stoAddress: '0x6666666666666666666666666666666666666666', + stoType: StoType.Tiered, + allowBeneficialInvestments: true, +}; + +const simpleParams: ToggleAllowBeneficialInvestmentsProcedureArgs = { + symbol: 'TEST1', + stoAddress: '0x5555555555555555555555555555555555555555', + stoType: StoType.Simple, + allowBeneficialInvestments: true, +}; + +const invalidSto = 'InvalidSto'; + +describe('ToggleAllowBeneficialInvestments', () => { + let target: ToggleAllowBeneficialInvestments; + let contextMock: MockManager; + let wrappersMock: MockManager; + let moduleWrapperFactoryMock: MockManager< + moduleWrapperFactoryModule.MockedModuleWrapperFactoryModule + >; + let simpleStoMock: MockManager; + let tieredStoMock: MockManager; + + // Mock factories + let simpleStoFactoryMock: MockManager; + let tieredStoFactoryMock: MockManager; + + let factoryMockSetup: Factories; + let securityTokenId: string; + + beforeEach(() => { + // Mock the context, wrappers, and tokenFactory to test toggle beneficial investments + contextMock = ImportMock.mockClass(contextModule, 'Context'); + wrappersMock = ImportMock.mockClass(wrappersModule, 'PolymathBase'); + moduleWrapperFactoryMock = ImportMock.mockClass( + moduleWrapperFactoryModule, + 'MockedModuleWrapperFactoryModule' + ); + + contextMock.set('contractWrappers', wrappersMock.getMockInstance()); + wrappersMock.set('moduleFactory', moduleWrapperFactoryMock.getMockInstance()); + + simpleStoFactoryMock = ImportMock.mockClass(simpleStoFactoryModule, 'SimpleStoFactory'); + + tieredStoFactoryMock = ImportMock.mockClass(tieredStoFactoryModule, 'TieredStoFactory'); + + factoryMockSetup = mockFactories(); + factoryMockSetup.simpleStoFactory = simpleStoFactoryMock.getMockInstance(); + factoryMockSetup.tieredStoFactory = tieredStoFactoryMock.getMockInstance(); + contextMock.set('factories', factoryMockSetup); + + tieredStoMock = ImportMock.mockClass(contractWrappersModule, 'USDTieredSTO_3_0_0'); + simpleStoMock = ImportMock.mockClass(contractWrappersModule, 'CappedSTO_3_0_0'); + + tieredStoMock.mock('allowBeneficialInvestments', Promise.resolve(false)); + simpleStoMock.mock('allowBeneficialInvestments', Promise.resolve(false)); + + securityTokenId = SecurityToken.generateId({ symbol: simpleParams.symbol }); + + moduleWrapperFactoryMock.mock('getModuleInstance', simpleStoMock.getMockInstance()); + + // Instantiate ToggleAllowBeneficialInvestments + target = new ToggleAllowBeneficialInvestments(simpleParams, contextMock.getMockInstance()); + }); + + afterEach(() => { + restore(); + }); + + describe('Types', () => { + test('should extend procedure and have ToggleAllowBeneficialInvestments type', async () => { + expect(target instanceof Procedure).toBe(true); + expect(target.type).toBe(ProcedureType.ToggleAllowBeneficialInvestments); + }); + }); + + describe('ToggleAllowBeneficialInvestments', () => { + test('should add the transaction to the queue to toggle allowed beneficial investments in a simple sto', async () => { + const addTransactionSpy = spy(target, 'addTransaction'); + simpleStoMock.mock( + 'changeAllowBeneficialInvestments', + Promise.resolve('ChangeAllowBeneficialInvestments') + ); + + // Real call + await target.prepareTransactions(); + + // Verifications + expect( + addTransactionSpy + .getCall(0) + .calledWith(simpleStoMock.getMockInstance().changeAllowBeneficialInvestments) + ).toEqual(true); + expect(addTransactionSpy.getCall(0).lastArg.tag).toEqual( + PolyTransactionTag.ChangeAllowBeneficialInvestments + ); + expect(addTransactionSpy.callCount).toEqual(1); + }); + + test('should add the transaction to the queue to toggle beneficial investments in a tiered sto', async () => { + moduleWrapperFactoryMock.mock('getModuleInstance', tieredStoMock.getMockInstance()); + target = new ToggleAllowBeneficialInvestments(tieredParams, contextMock.getMockInstance()); + + const addTransactionSpy = spy(target, 'addTransaction'); + tieredStoMock.mock( + 'changeAllowBeneficialInvestments', + Promise.resolve('ChangeAllowBeneficialInvestments') + ); + + // Real call + await target.prepareTransactions(); + + // Verifications + expect( + addTransactionSpy + .getCall(0) + .calledWith(tieredStoMock.getMockInstance().changeAllowBeneficialInvestments) + ).toEqual(true); + expect(addTransactionSpy.getCall(0).lastArg.tag).toEqual( + PolyTransactionTag.ChangeAllowBeneficialInvestments + ); + expect(addTransactionSpy.callCount).toEqual(1); + }); + + test('should throw if there is an invalid sto address', async () => { + target = new ToggleAllowBeneficialInvestments( + { + ...simpleParams, + stoAddress: 'invalid', + }, + contextMock.getMockInstance() + ); + await expect(target.prepareTransactions()).rejects.toThrow( + new PolymathError({ + code: ErrorCode.InvalidAddress, + message: `Invalid STO address invalid`, + }) + ); + }); + + test('should throw if trying to disallow beneficial investments', async () => { + target = new ToggleAllowBeneficialInvestments( + { + ...simpleParams, + allowBeneficialInvestments: false, + }, + contextMock.getMockInstance() + ); + + await expect(target.prepareTransactions()).rejects.toThrow( + new PolymathError({ + code: ErrorCode.ProcedureValidationError, + message: `Beneficial investments are already disallowed`, + }) + ); + }); + + test('should throw if beneficial investments are already allowed', async () => { + simpleStoMock.mock('allowBeneficialInvestments', Promise.resolve(true)); + + await expect(target.prepareTransactions()).rejects.toThrow( + new PolymathError({ + code: ErrorCode.ProcedureValidationError, + message: `Beneficial investments are already allowed`, + }) + ); + }); + + test('should throw if there is an invalid sto type', async () => { + target = new ToggleAllowBeneficialInvestments( + { + ...simpleParams, + stoType: invalidSto as StoType, + }, + contextMock.getMockInstance() + ); + await expect(target.prepareTransactions()).rejects.toThrow( + new PolymathError({ + code: ErrorCode.ProcedureValidationError, + message: `Invalid STO type ${invalidSto}`, + }) + ); + }); + + test("should throw if the STO doesn't exist", async () => { + moduleWrapperFactoryMock.mock('getModuleInstance', undefined); + await expect(target.prepareTransactions()).rejects.toThrow( + new PolymathError({ + code: ErrorCode.ProcedureValidationError, + message: `STO ${simpleParams.stoAddress} is either archived or hasn't been launched`, + }) + ); + }); + + test('should successfully create toggleAllowBeneficialInvestments resolver with simple sto params', async () => { + const refreshStub = simpleStoFactoryMock.mock('refresh', Promise.resolve()); + await toggleAllowBeneficalInvestmentsModule.createToggleAllowBeneficialInvestmentsResolver( + factoryMockSetup, + simpleParams.symbol, + simpleParams.stoType, + simpleParams.stoAddress + )(); + expect( + refreshStub.getCall(0).calledWithExactly( + SimpleSto.generateId({ + securityTokenId, + stoType: StoType.Simple, + address: simpleParams.stoAddress, + }) + ) + ).toEqual(true); + expect(refreshStub.callCount).toEqual(1); + }); + + test('should successfully create toggleAllowBeneficialInvestments resolver with tiered sto params', async () => { + target = new ToggleAllowBeneficialInvestments(tieredParams, contextMock.getMockInstance()); + const refreshStub = tieredStoFactoryMock.mock('refresh', Promise.resolve()); + await toggleAllowBeneficalInvestmentsModule.createToggleAllowBeneficialInvestmentsResolver( + factoryMockSetup, + tieredParams.symbol, + tieredParams.stoType, + tieredParams.stoAddress + )(); + expect( + refreshStub.getCall(0).calledWithExactly( + TieredSto.generateId({ + securityTokenId, + stoType: StoType.Tiered, + address: tieredParams.stoAddress, + }) + ) + ).toEqual(true); + expect(refreshStub.callCount).toEqual(1); + }); + }); +}); diff --git a/src/procedures/__tests__/ToggleAllowPreIssuing.ts b/src/procedures/__tests__/ToggleAllowPreIssuing.ts new file mode 100644 index 0000000..83fd58a --- /dev/null +++ b/src/procedures/__tests__/ToggleAllowPreIssuing.ts @@ -0,0 +1,337 @@ +import { ImportMock, MockManager } from 'ts-mock-imports'; +import { restore, spy } from 'sinon'; +import * as contractWrappersModule from '@polymathnetwork/contract-wrappers'; +import { ContractVersion } from '@polymathnetwork/contract-wrappers'; +import { ToggleAllowPreIssuing } from '../ToggleAllowPreIssuing'; +import { Procedure } from '../Procedure'; +import { PolymathError } from '../../PolymathError'; +import { + ErrorCode, + ToggleAllowPreIssuingProcedureArgs, + PolyTransactionTag, + ProcedureType, + StoType, +} from '../../types'; +import * as toggleAllowPreIssuingModule from '../ToggleAllowPreIssuing'; +import * as simpleStoFactoryModule from '../../entities/factories/SimpleStoFactory'; +import * as tieredStoFactoryModule from '../../entities/factories/TieredStoFactory'; +import * as contextModule from '../../Context'; +import * as wrappersModule from '../../PolymathBase'; +import * as moduleWrapperFactoryModule from '../../testUtils/MockedModuleWrapperFactoryModule'; +import { mockFactories } from '../../testUtils/mockFactories'; +import { Factories } from '../../Context'; +import { SimpleSto, SecurityToken, TieredSto } from '../../entities'; + +const tieredParams: ToggleAllowPreIssuingProcedureArgs = { + symbol: 'TEST1', + stoAddress: '0x6666666666666666666666666666666666666666', + stoType: StoType.Tiered, + allowPreIssuing: true, +}; + +const simpleParams: ToggleAllowPreIssuingProcedureArgs = { + symbol: 'TEST1', + stoAddress: '0x5555555555555555555555555555555555555555', + stoType: StoType.Simple, + allowPreIssuing: true, +}; + +const invalidSto = 'InvalidSto'; + +describe('ToggleAllowPreIssuing', () => { + let target: ToggleAllowPreIssuing; + let contextMock: MockManager; + let wrappersMock: MockManager; + let moduleWrapperFactoryMock: MockManager< + moduleWrapperFactoryModule.MockedModuleWrapperFactoryModule + >; + let simpleSto_3_0_0_Mock: MockManager; + let tieredSto_3_0_0_Mock: MockManager; + + let simpleSto_3_1_0_Mock: MockManager; + let tieredSto_3_1_0_Mock: MockManager; + + // Mock factories + let simpleStoFactoryMock: MockManager; + let tieredStoFactoryMock: MockManager; + + let factoryMockSetup: Factories; + let securityTokenId: string; + + beforeEach(() => { + // Mock the context, wrappers, and tokenFactory to test allow pre issuing + contextMock = ImportMock.mockClass(contextModule, 'Context'); + wrappersMock = ImportMock.mockClass(wrappersModule, 'PolymathBase'); + moduleWrapperFactoryMock = ImportMock.mockClass( + moduleWrapperFactoryModule, + 'MockedModuleWrapperFactoryModule' + ); + + contextMock.set('contractWrappers', wrappersMock.getMockInstance()); + wrappersMock.set('moduleFactory', moduleWrapperFactoryMock.getMockInstance()); + + simpleStoFactoryMock = ImportMock.mockClass(simpleStoFactoryModule, 'SimpleStoFactory'); + + tieredStoFactoryMock = ImportMock.mockClass(tieredStoFactoryModule, 'TieredStoFactory'); + + factoryMockSetup = mockFactories(); + factoryMockSetup.simpleStoFactory = simpleStoFactoryMock.getMockInstance(); + factoryMockSetup.tieredStoFactory = tieredStoFactoryMock.getMockInstance(); + contextMock.set('factories', factoryMockSetup); + + tieredSto_3_0_0_Mock = ImportMock.mockClass(contractWrappersModule, 'USDTieredSTO_3_0_0'); + tieredSto_3_1_0_Mock = ImportMock.mockClass(contractWrappersModule, 'USDTieredSTO_3_1_0'); + simpleSto_3_0_0_Mock = ImportMock.mockClass(contractWrappersModule, 'CappedSTO_3_0_0'); + simpleSto_3_1_0_Mock = ImportMock.mockClass(contractWrappersModule, 'CappedSTO_3_1_0'); + + tieredSto_3_1_0_Mock.mock('preMintAllowed', Promise.resolve(false)); + simpleSto_3_1_0_Mock.mock('preMintAllowed', Promise.resolve(false)); + + securityTokenId = SecurityToken.generateId({ symbol: simpleParams.symbol }); + + moduleWrapperFactoryMock.mock('getModuleInstance', simpleSto_3_1_0_Mock.getMockInstance()); + + // Instantiate ToggleAllowPreIssuing + target = new ToggleAllowPreIssuing(simpleParams, contextMock.getMockInstance()); + }); + + afterEach(() => { + restore(); + }); + + describe('Types', () => { + test('should extend procedure and have ToggleAllowPreIssuing type', async () => { + expect(target instanceof Procedure).toBe(true); + expect(target.type).toBe(ProcedureType.ToggleAllowPreIssuing); + }); + }); + + describe('ToggleAllowPreIssuing', () => { + test('should add the transaction to the queue to allow pre issuing in a simple sto', async () => { + const addTransactionSpy = spy(target, 'addTransaction'); + simpleSto_3_1_0_Mock.mock('allowPreMinting', Promise.resolve('AllowPreMinting')); + + // Real call + await target.prepareTransactions(); + + // Verifications + expect( + addTransactionSpy + .getCall(0) + .calledWith(simpleSto_3_1_0_Mock.getMockInstance().allowPreMinting) + ).toEqual(true); + expect(addTransactionSpy.getCall(0).lastArg.tag).toEqual(PolyTransactionTag.AllowPreMinting); + expect(addTransactionSpy.callCount).toEqual(1); + }); + + test('should add the transaction to the queue to revoke pre issuing in a simple sto', async () => { + target = new ToggleAllowPreIssuing( + { ...simpleParams, allowPreIssuing: false }, + contextMock.getMockInstance() + ); + simpleSto_3_1_0_Mock.mock('preMintAllowed', Promise.resolve(true)); + const addTransactionSpy = spy(target, 'addTransaction'); + simpleSto_3_1_0_Mock.mock('revokePreMintFlag', Promise.resolve('RevokePreMintFlag')); + + // Real call + await target.prepareTransactions(); + + // Verifications + expect( + addTransactionSpy + .getCall(0) + .calledWith(simpleSto_3_1_0_Mock.getMockInstance().revokePreMintFlag) + ).toEqual(true); + expect(addTransactionSpy.getCall(0).lastArg.tag).toEqual(PolyTransactionTag.RevokePreMinting); + expect(addTransactionSpy.callCount).toEqual(1); + }); + + test('should add the transaction to the queue to allow pre issuing in a tiered sto', async () => { + moduleWrapperFactoryMock.mock('getModuleInstance', tieredSto_3_1_0_Mock.getMockInstance()); + target = new ToggleAllowPreIssuing(tieredParams, contextMock.getMockInstance()); + + const addTransactionSpy = spy(target, 'addTransaction'); + tieredSto_3_1_0_Mock.mock('allowPreMinting', Promise.resolve('ChangeAllowPreIssuing')); + + // Real call + await target.prepareTransactions(); + + // Verifications + expect( + addTransactionSpy + .getCall(0) + .calledWith(tieredSto_3_1_0_Mock.getMockInstance().allowPreMinting) + ).toEqual(true); + expect(addTransactionSpy.getCall(0).lastArg.tag).toEqual(PolyTransactionTag.AllowPreMinting); + expect(addTransactionSpy.callCount).toEqual(1); + }); + + test('should add the transaction to the queue to revoke pre issuing in a tiered sto', async () => { + target = new ToggleAllowPreIssuing( + { ...tieredParams, allowPreIssuing: false }, + contextMock.getMockInstance() + ); + simpleSto_3_1_0_Mock.mock('preMintAllowed', Promise.resolve(true)); + const addTransactionSpy = spy(target, 'addTransaction'); + simpleSto_3_1_0_Mock.mock('revokePreMintFlag', Promise.resolve('RevokePreMintFlag')); + + // Real call + await target.prepareTransactions(); + + // Verifications + expect( + addTransactionSpy + .getCall(0) + .calledWith(simpleSto_3_1_0_Mock.getMockInstance().revokePreMintFlag) + ).toEqual(true); + expect(addTransactionSpy.getCall(0).lastArg.tag).toEqual(PolyTransactionTag.RevokePreMinting); + expect(addTransactionSpy.callCount).toEqual(1); + }); + + test('should throw if there is an invalid sto address', async () => { + target = new ToggleAllowPreIssuing( + { + ...simpleParams, + stoAddress: 'invalid', + }, + contextMock.getMockInstance() + ); + await expect(target.prepareTransactions()).rejects.toThrow( + new PolymathError({ + code: ErrorCode.InvalidAddress, + message: `Invalid STO address invalid`, + }) + ); + }); + + test('should throw if trying to disallow beneficial investments', async () => { + target = new ToggleAllowPreIssuing( + { + ...simpleParams, + allowPreIssuing: false, + }, + contextMock.getMockInstance() + ); + + await expect(target.prepareTransactions()).rejects.toThrow( + new PolymathError({ + code: ErrorCode.ProcedureValidationError, + message: `Pre-minting is already disallowed`, + }) + ); + }); + + test('should throw if beneficial investments are already allowed', async () => { + simpleSto_3_1_0_Mock.mock('preMintAllowed', Promise.resolve(true)); + + await expect(target.prepareTransactions()).rejects.toThrow( + new PolymathError({ + code: ErrorCode.ProcedureValidationError, + message: `Pre-minting is already allowed`, + }) + ); + }); + + test('should throw if there is an invalid sto type', async () => { + target = new ToggleAllowPreIssuing( + { + ...simpleParams, + stoType: invalidSto as StoType, + }, + contextMock.getMockInstance() + ); + await expect(target.prepareTransactions()).rejects.toThrow( + new PolymathError({ + code: ErrorCode.ProcedureValidationError, + message: `Invalid STO type ${invalidSto}`, + }) + ); + }); + + test("should throw if the STO doesn't exist, with simple sto params", async () => { + moduleWrapperFactoryMock.mock('getModuleInstance', undefined); + await expect(target.prepareTransactions()).rejects.toThrow( + new PolymathError({ + code: ErrorCode.ProcedureValidationError, + message: `STO ${simpleParams.stoAddress} is either archived or hasn't been launched`, + }) + ); + }); + + test("should throw if the STO doesn't exist, with tiered sto params", async () => { + target = new ToggleAllowPreIssuing(tieredParams, contextMock.getMockInstance()); + moduleWrapperFactoryMock.mock('getModuleInstance', undefined); + await expect(target.prepareTransactions()).rejects.toThrow( + new PolymathError({ + code: ErrorCode.ProcedureValidationError, + message: `STO ${tieredParams.stoAddress} is either archived or hasn't been launched`, + }) + ); + }); + + test('should throw error if the simple sto version is 3_0_0', async () => { + moduleWrapperFactoryMock.mock('getModuleInstance', simpleSto_3_0_0_Mock.getMockInstance()); + simpleSto_3_0_0_Mock.set('contractVersion', ContractVersion.V3_0_0); + + await expect(target.prepareTransactions()).rejects.toThrow( + new PolymathError({ + code: ErrorCode.IncorrectVersion, + message: 'STO version is 3.0.0. Version 3.1.0 or greater is required for pre-minting', + }) + ); + }); + + test('should throw error if the tiered sto version is 3_0_0', async () => { + moduleWrapperFactoryMock.mock('getModuleInstance', tieredSto_3_0_0_Mock.getMockInstance()); + tieredSto_3_0_0_Mock.set('contractVersion', ContractVersion.V3_0_0); + + await expect(target.prepareTransactions()).rejects.toThrow( + new PolymathError({ + code: ErrorCode.IncorrectVersion, + message: 'STO version is 3.0.0. Version 3.1.0 or greater is required for pre-minting', + }) + ); + }); + + test('should successfully create toggleAllowPreIssuing resolver with simple sto params', async () => { + const refreshStub = simpleStoFactoryMock.mock('refresh', Promise.resolve()); + await toggleAllowPreIssuingModule.createToggleAllowPreIssuingResolver( + factoryMockSetup, + simpleParams.symbol, + simpleParams.stoType, + simpleParams.stoAddress + )(); + expect( + refreshStub.getCall(0).calledWithExactly( + SimpleSto.generateId({ + securityTokenId, + stoType: StoType.Simple, + address: simpleParams.stoAddress, + }) + ) + ).toEqual(true); + expect(refreshStub.callCount).toEqual(1); + }); + + test('should successfully create toggleAllowPreIssuing resolver with tiered sto params', async () => { + target = new ToggleAllowPreIssuing(tieredParams, contextMock.getMockInstance()); + const refreshStub = tieredStoFactoryMock.mock('refresh', Promise.resolve()); + await toggleAllowPreIssuingModule.createToggleAllowPreIssuingResolver( + factoryMockSetup, + tieredParams.symbol, + tieredParams.stoType, + tieredParams.stoAddress + )(); + expect( + refreshStub.getCall(0).calledWithExactly( + TieredSto.generateId({ + securityTokenId, + stoType: StoType.Tiered, + address: tieredParams.stoAddress, + }) + ) + ).toEqual(true); + expect(refreshStub.callCount).toEqual(1); + }); + }); +}); From 3364ea853632603efdcb2610fabab2b6cb6b3631 Mon Sep 17 00:00:00 2001 From: Victor Wiebe Date: Mon, 30 Dec 2019 13:20:46 -0800 Subject: [PATCH 21/33] fix: remove unnecessary import --- src/procedures/__tests__/InvestInSimpleSto.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/procedures/__tests__/InvestInSimpleSto.ts b/src/procedures/__tests__/InvestInSimpleSto.ts index ac14ba2..093ddc8 100644 --- a/src/procedures/__tests__/InvestInSimpleSto.ts +++ b/src/procedures/__tests__/InvestInSimpleSto.ts @@ -1,7 +1,7 @@ import { ImportMock, MockManager } from 'ts-mock-imports'; import { restore, spy } from 'sinon'; import * as contractWrappersModule from '@polymathnetwork/contract-wrappers'; -import { BigNumber, TransferStatusCode } from '@polymathnetwork/contract-wrappers'; +import { BigNumber } from '@polymathnetwork/contract-wrappers'; import { InvestInSimpleSto } from '../InvestInSimpleSto'; import { Procedure } from '../Procedure'; import { PolymathError } from '../../PolymathError'; From da08c795dc467c225228e7a9127edb666e41d587 Mon Sep 17 00:00:00 2001 From: Victor Wiebe Date: Tue, 31 Dec 2019 07:05:01 -0800 Subject: [PATCH 22/33] fix: yarn issues --- src/procedures/__tests__/ToggleAllowBeneficialInvestments.ts | 1 + src/procedures/__tests__/ToggleAllowPreIssuing.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/procedures/__tests__/ToggleAllowBeneficialInvestments.ts b/src/procedures/__tests__/ToggleAllowBeneficialInvestments.ts index ffcc661..63d32d4 100644 --- a/src/procedures/__tests__/ToggleAllowBeneficialInvestments.ts +++ b/src/procedures/__tests__/ToggleAllowBeneficialInvestments.ts @@ -1,3 +1,4 @@ +/* eslint-disable import/no-duplicates */ import { ImportMock, MockManager } from 'ts-mock-imports'; import { restore, spy } from 'sinon'; import * as contractWrappersModule from '@polymathnetwork/contract-wrappers'; diff --git a/src/procedures/__tests__/ToggleAllowPreIssuing.ts b/src/procedures/__tests__/ToggleAllowPreIssuing.ts index 83fd58a..f96d67b 100644 --- a/src/procedures/__tests__/ToggleAllowPreIssuing.ts +++ b/src/procedures/__tests__/ToggleAllowPreIssuing.ts @@ -1,3 +1,4 @@ +/* eslint-disable import/no-duplicates */ import { ImportMock, MockManager } from 'ts-mock-imports'; import { restore, spy } from 'sinon'; import * as contractWrappersModule from '@polymathnetwork/contract-wrappers'; From c0a539229449921501c473439bcf21922f25bc20 Mon Sep 17 00:00:00 2001 From: Victor Wiebe Date: Tue, 31 Dec 2019 07:07:16 -0800 Subject: [PATCH 23/33] fix: yarn issues --- src/procedures/__tests__/FinalizeSto.ts | 1 + src/procedures/__tests__/InvestInSimpleSto.ts | 1 + src/procedures/__tests__/InvestInTieredSto.ts | 1 + 3 files changed, 3 insertions(+) diff --git a/src/procedures/__tests__/FinalizeSto.ts b/src/procedures/__tests__/FinalizeSto.ts index 8b06110..5e950c8 100644 --- a/src/procedures/__tests__/FinalizeSto.ts +++ b/src/procedures/__tests__/FinalizeSto.ts @@ -1,3 +1,4 @@ +/* eslint-disable import/no-duplicates */ import { ImportMock, MockManager } from 'ts-mock-imports'; import { restore, spy } from 'sinon'; import * as contractWrappersModule from '@polymathnetwork/contract-wrappers'; diff --git a/src/procedures/__tests__/InvestInSimpleSto.ts b/src/procedures/__tests__/InvestInSimpleSto.ts index 093ddc8..31965cc 100644 --- a/src/procedures/__tests__/InvestInSimpleSto.ts +++ b/src/procedures/__tests__/InvestInSimpleSto.ts @@ -1,3 +1,4 @@ +/* eslint-disable import/no-duplicates */ import { ImportMock, MockManager } from 'ts-mock-imports'; import { restore, spy } from 'sinon'; import * as contractWrappersModule from '@polymathnetwork/contract-wrappers'; diff --git a/src/procedures/__tests__/InvestInTieredSto.ts b/src/procedures/__tests__/InvestInTieredSto.ts index 2a0d72b..5e61f30 100644 --- a/src/procedures/__tests__/InvestInTieredSto.ts +++ b/src/procedures/__tests__/InvestInTieredSto.ts @@ -1,3 +1,4 @@ +/* eslint-disable import/no-duplicates */ import { ImportMock, MockManager } from 'ts-mock-imports'; import { restore, spy } from 'sinon'; import * as contractWrappersModule from '@polymathnetwork/contract-wrappers'; From 07ad55756230852e2108b5e095841e6d179c9726 Mon Sep 17 00:00:00 2001 From: Victor Wiebe Date: Mon, 6 Jan 2020 09:58:40 -0800 Subject: [PATCH 24/33] fix: review comments, unnecessary code and internal func --- src/procedures/FinalizeSto.ts | 17 +++++++++-------- src/procedures/__tests__/InvestInSimpleSto.ts | 2 -- src/procedures/__tests__/InvestInTieredSto.ts | 4 ---- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/src/procedures/FinalizeSto.ts b/src/procedures/FinalizeSto.ts index dbbe621..2d4e6b2 100644 --- a/src/procedures/FinalizeSto.ts +++ b/src/procedures/FinalizeSto.ts @@ -75,12 +75,7 @@ export class FinalizeSto extends Procedure { }); } - function throwStoModuleError() { - throw new PolymathError({ - code: ErrorCode.ProcedureValidationError, - message: `STO ${stoAddress} is either archived or hasn't been launched`, - }); - } + const stoModuleErrorMessage = `STO ${stoAddress} is either archived or hasn't been launched`; let stoModule; let remainingTokens: BigNumber; @@ -93,7 +88,10 @@ export class FinalizeSto extends Procedure { }); if (!stoModule) { - throwStoModuleError(); + throw new PolymathError({ + code: ErrorCode.ProcedureValidationError, + message: stoModuleErrorMessage, + }); } if (isCappedSTO_3_0_0(stoModule)) { @@ -116,7 +114,10 @@ export class FinalizeSto extends Procedure { }); if (!stoModule) { - throwStoModuleError(); + throw new PolymathError({ + code: ErrorCode.ProcedureValidationError, + message: stoModuleErrorMessage, + }); } const { tokensSold, capPerTier } = await stoModule.getSTODetails(); diff --git a/src/procedures/__tests__/InvestInSimpleSto.ts b/src/procedures/__tests__/InvestInSimpleSto.ts index 31965cc..d730efd 100644 --- a/src/procedures/__tests__/InvestInSimpleSto.ts +++ b/src/procedures/__tests__/InvestInSimpleSto.ts @@ -154,8 +154,6 @@ describe('InvestInSimpleSto', () => { const addProcedureSpy = spy(target, 'addProcedure'); simpleStoMock.mock('buyTokensWithPoly', Promise.resolve('BuyTokensWithPoly')); - securityTokenMock.mock('balanceOf', Promise.resolve(new BigNumber(10))); - // Real call await target.prepareTransactions(); diff --git a/src/procedures/__tests__/InvestInTieredSto.ts b/src/procedures/__tests__/InvestInTieredSto.ts index 5e61f30..3e264f6 100644 --- a/src/procedures/__tests__/InvestInTieredSto.ts +++ b/src/procedures/__tests__/InvestInTieredSto.ts @@ -162,8 +162,6 @@ describe('InvestInTieredSto', () => { const addProcedureSpy = spy(target, 'addProcedure'); tieredStoMock.mock('buyWithUSDRateLimited', Promise.resolve('BuyWithUSDRateLimited')); - securityTokenMock.mock('balanceOf', Promise.resolve(new BigNumber(10))); - // Real call await target.prepareTransactions(); @@ -194,8 +192,6 @@ describe('InvestInTieredSto', () => { const addProcedureSpy = spy(target, 'addProcedure'); tieredStoMock.mock('buyWithUSDRateLimited', Promise.resolve('BuyWithUSDRateLimited')); - securityTokenMock.mock('balanceOf', Promise.resolve(new BigNumber(10))); - // Real call await target.prepareTransactions(); From daa2c2c83d70de8b814c8ba9958e34f9991a6d6b Mon Sep 17 00:00:00 2001 From: Victor Wiebe Date: Mon, 6 Jan 2020 10:18:32 -0800 Subject: [PATCH 25/33] fix: review comments and feedback --- src/procedures/ToggleAllowPreIssuing.ts | 17 +++++++++-------- .../ToggleAllowBeneficialInvestments.ts | 8 ++++---- .../__tests__/ToggleAllowPreIssuing.ts | 12 ++++++------ 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/procedures/ToggleAllowPreIssuing.ts b/src/procedures/ToggleAllowPreIssuing.ts index ad620f1..7f03176 100644 --- a/src/procedures/ToggleAllowPreIssuing.ts +++ b/src/procedures/ToggleAllowPreIssuing.ts @@ -72,12 +72,7 @@ export class ToggleAllowPreIssuing extends Procedure { ); }); - test('should throw if trying to disallow beneficial investments', async () => { + test('should throw if trying to disallow beneficial investments when they are already disallowed', async () => { target = new ToggleAllowBeneficialInvestments( { ...simpleParams, @@ -182,7 +182,7 @@ describe('ToggleAllowBeneficialInvestments', () => { ); }); - test('should throw if beneficial investments are already allowed', async () => { + test('should throw if trying to allow beneficial investments when they are already allowed', async () => { simpleStoMock.mock('allowBeneficialInvestments', Promise.resolve(true)); await expect(target.prepareTransactions()).rejects.toThrow( @@ -219,7 +219,7 @@ describe('ToggleAllowBeneficialInvestments', () => { ); }); - test('should successfully create toggleAllowBeneficialInvestments resolver with simple sto params', async () => { + test('should refresh the simple STO', async () => { const refreshStub = simpleStoFactoryMock.mock('refresh', Promise.resolve()); await toggleAllowBeneficalInvestmentsModule.createToggleAllowBeneficialInvestmentsResolver( factoryMockSetup, @@ -239,7 +239,7 @@ describe('ToggleAllowBeneficialInvestments', () => { expect(refreshStub.callCount).toEqual(1); }); - test('should successfully create toggleAllowBeneficialInvestments resolver with tiered sto params', async () => { + test('should refresh the tiered STO', async () => { target = new ToggleAllowBeneficialInvestments(tieredParams, contextMock.getMockInstance()); const refreshStub = tieredStoFactoryMock.mock('refresh', Promise.resolve()); await toggleAllowBeneficalInvestmentsModule.createToggleAllowBeneficialInvestmentsResolver( diff --git a/src/procedures/__tests__/ToggleAllowPreIssuing.ts b/src/procedures/__tests__/ToggleAllowPreIssuing.ts index f96d67b..7a412bc 100644 --- a/src/procedures/__tests__/ToggleAllowPreIssuing.ts +++ b/src/procedures/__tests__/ToggleAllowPreIssuing.ts @@ -205,7 +205,7 @@ describe('ToggleAllowPreIssuing', () => { ); }); - test('should throw if trying to disallow beneficial investments', async () => { + test('should throw if trying to disallow pre issuing when pre issuing is already disallowed', async () => { target = new ToggleAllowPreIssuing( { ...simpleParams, @@ -222,7 +222,7 @@ describe('ToggleAllowPreIssuing', () => { ); }); - test('should throw if beneficial investments are already allowed', async () => { + test('should throw if trying to allow pre issuing when pre issuing is already allowed', async () => { simpleSto_3_1_0_Mock.mock('preMintAllowed', Promise.resolve(true)); await expect(target.prepareTransactions()).rejects.toThrow( @@ -249,7 +249,7 @@ describe('ToggleAllowPreIssuing', () => { ); }); - test("should throw if the STO doesn't exist, with simple sto params", async () => { + test("should throw if the simple STO doesn't exist", async () => { moduleWrapperFactoryMock.mock('getModuleInstance', undefined); await expect(target.prepareTransactions()).rejects.toThrow( new PolymathError({ @@ -259,7 +259,7 @@ describe('ToggleAllowPreIssuing', () => { ); }); - test("should throw if the STO doesn't exist, with tiered sto params", async () => { + test("should throw if the tiered STO doesn't exist", async () => { target = new ToggleAllowPreIssuing(tieredParams, contextMock.getMockInstance()); moduleWrapperFactoryMock.mock('getModuleInstance', undefined); await expect(target.prepareTransactions()).rejects.toThrow( @@ -294,7 +294,7 @@ describe('ToggleAllowPreIssuing', () => { ); }); - test('should successfully create toggleAllowPreIssuing resolver with simple sto params', async () => { + test('should refresh the simple STO', async () => { const refreshStub = simpleStoFactoryMock.mock('refresh', Promise.resolve()); await toggleAllowPreIssuingModule.createToggleAllowPreIssuingResolver( factoryMockSetup, @@ -314,7 +314,7 @@ describe('ToggleAllowPreIssuing', () => { expect(refreshStub.callCount).toEqual(1); }); - test('should successfully create toggleAllowPreIssuing resolver with tiered sto params', async () => { + test('should refresh the tiered STO', async () => { target = new ToggleAllowPreIssuing(tieredParams, contextMock.getMockInstance()); const refreshStub = tieredStoFactoryMock.mock('refresh', Promise.resolve()); await toggleAllowPreIssuingModule.createToggleAllowPreIssuingResolver( From 25bca45ae0c75745fa20580b51fb106b8af95c1f Mon Sep 17 00:00:00 2001 From: Shuffledex Date: Tue, 7 Jan 2020 09:23:10 -0300 Subject: [PATCH 26/33] fix: feedback improvements --- .../__tests__/PullDividendPayment.ts | 38 +++++++++++-------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/src/procedures/__tests__/PullDividendPayment.ts b/src/procedures/__tests__/PullDividendPayment.ts index 977a55e..a02ec29 100644 --- a/src/procedures/__tests__/PullDividendPayment.ts +++ b/src/procedures/__tests__/PullDividendPayment.ts @@ -20,6 +20,8 @@ const params = { dividendIndex: 0, }; +const addresses = ['0x01', '0x02', '0x03', '0x04']; + describe('PullDividendPayment', () => { let target: PullDividendPayment; let contextMock: MockManager; @@ -53,7 +55,7 @@ describe('PullDividendPayment', () => { factoriesMockedSetup = mockFactories(); factoriesMockedSetup.dividendDistributionFactory = dividendFactoryMock.getMockInstance(); contextMock.set('factories', factoriesMockedSetup); - contextMock.set('currentWallet', new Wallet({ address: () => Promise.resolve('0x01') })); + contextMock.set('currentWallet', new Wallet({ address: () => Promise.resolve(addresses[0]) })); target = new PullDividendPayment(params, contextMock.getMockInstance()); }); @@ -96,7 +98,10 @@ describe('PullDividendPayment', () => { }); test('should throw if the owner address is not a shareholder', async () => { - contextMock.set('currentWallet', new Wallet({ address: () => Promise.resolve('0x04') })); + contextMock.set( + 'currentWallet', + new Wallet({ address: () => Promise.resolve(addresses[3]) }) + ); wrappersMock.mock( 'getAttachedModules', @@ -106,7 +111,7 @@ describe('PullDividendPayment', () => { wrappersMock.mock( 'getDividend', Promise.resolve({ - shareholders: [{ address: '0x01', paymentReceived: false, excluded: false }], + shareholders: [{ address: addresses[3], paymentReceived: false, excluded: false }], }) ); @@ -114,8 +119,9 @@ describe('PullDividendPayment', () => { await expect(target.prepareTransactions()).rejects.toThrowError( new PolymathError({ code: ErrorCode.ProcedureValidationError, - message: - 'Current wallet 0x04 cannot receive dividend payments. Reason: not a shareholder', + message: `Current wallet ${ + addresses[3] + } cannot receive dividend payments. Reason: not a shareholder`, }) ); }); @@ -129,7 +135,7 @@ describe('PullDividendPayment', () => { wrappersMock.mock( 'getDividend', Promise.resolve({ - shareholders: [{ address: '0x01', paymentReceived: true, excluded: false }], + shareholders: [{ address: addresses[0], paymentReceived: true, excluded: false }], }) ); @@ -137,8 +143,9 @@ describe('PullDividendPayment', () => { await expect(target.prepareTransactions()).rejects.toThrowError( new PolymathError({ code: ErrorCode.ProcedureValidationError, - message: - 'Current wallet 0x01 cannot receive dividend payments. Reason: already received payment', + message: `Current wallet ${ + addresses[0] + } cannot receive dividend payments. Reason: already received payment`, }) ); }); @@ -152,7 +159,7 @@ describe('PullDividendPayment', () => { wrappersMock.mock( 'getDividend', Promise.resolve({ - shareholders: [{ address: '0x01', paymentReceived: false, excluded: true }], + shareholders: [{ address: addresses[0], paymentReceived: false, excluded: true }], }) ); @@ -160,13 +167,14 @@ describe('PullDividendPayment', () => { await expect(target.prepareTransactions()).rejects.toThrowError( new PolymathError({ code: ErrorCode.ProcedureValidationError, - message: - 'Current wallet 0x01 cannot receive dividend payments. Reason: address belongs to exclusion list', + message: `Current wallet ${ + addresses[0] + } cannot receive dividend payments. Reason: address belongs to exclusion list`, }) ); }); - test('should add a transaction to pull a dividend payment', async () => { + test('should add a transaction to pull dividend payments', async () => { wrappersMock.mock( 'getAttachedModules', Promise.resolve([erc20DividendsMock.getMockInstance()]) @@ -176,9 +184,9 @@ describe('PullDividendPayment', () => { 'getDividend', Promise.resolve({ shareholders: [ - { address: '0x01', paymentReceived: false, excluded: false }, - { address: '0x02', paymentReceived: false, excluded: false }, - { address: '0x03', paymentReceived: true, excluded: false }, + { address: addresses[0], paymentReceived: false, excluded: false }, + { address: addresses[1], paymentReceived: false, excluded: false }, + { address: addresses[2], paymentReceived: true, excluded: false }, ], }) ); From bfaeff91641e60d421e161926bb33e2c840f8e1a Mon Sep 17 00:00:00 2001 From: Shuffledex Date: Tue, 7 Jan 2020 09:38:40 -0300 Subject: [PATCH 27/33] fix: travis CI fix --- src/procedures/__tests__/PullDividendPayment.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/procedures/__tests__/PullDividendPayment.ts b/src/procedures/__tests__/PullDividendPayment.ts index a02ec29..aaf99f3 100644 --- a/src/procedures/__tests__/PullDividendPayment.ts +++ b/src/procedures/__tests__/PullDividendPayment.ts @@ -111,7 +111,7 @@ describe('PullDividendPayment', () => { wrappersMock.mock( 'getDividend', Promise.resolve({ - shareholders: [{ address: addresses[3], paymentReceived: false, excluded: false }], + shareholders: [{ address: addresses[0], paymentReceived: false, excluded: false }], }) ); From 0410e697ef8f9d351d2c17e1a2e78d1ba6cd1f28 Mon Sep 17 00:00:00 2001 From: Victor Wiebe Date: Tue, 7 Jan 2020 06:07:36 -0800 Subject: [PATCH 28/33] fix: make error a const --- src/procedures/ToggleAllowPreIssuing.ts | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/procedures/ToggleAllowPreIssuing.ts b/src/procedures/ToggleAllowPreIssuing.ts index 7f03176..e55569c 100644 --- a/src/procedures/ToggleAllowPreIssuing.ts +++ b/src/procedures/ToggleAllowPreIssuing.ts @@ -72,7 +72,10 @@ export class ToggleAllowPreIssuing extends Procedure Date: Tue, 7 Jan 2020 06:11:20 -0800 Subject: [PATCH 29/33] fix: sto module error issue --- src/procedures/FinalizeSto.ts | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/procedures/FinalizeSto.ts b/src/procedures/FinalizeSto.ts index 2d4e6b2..032accc 100644 --- a/src/procedures/FinalizeSto.ts +++ b/src/procedures/FinalizeSto.ts @@ -75,7 +75,10 @@ export class FinalizeSto extends Procedure { }); } - const stoModuleErrorMessage = `STO ${stoAddress} is either archived or hasn't been launched`; + const stoModuleError = new PolymathError({ + code: ErrorCode.ProcedureValidationError, + message: `STO ${stoAddress} is either archived or hasn't been launched`, + }); let stoModule; let remainingTokens: BigNumber; @@ -88,10 +91,7 @@ export class FinalizeSto extends Procedure { }); if (!stoModule) { - throw new PolymathError({ - code: ErrorCode.ProcedureValidationError, - message: stoModuleErrorMessage, - }); + throw stoModuleError; } if (isCappedSTO_3_0_0(stoModule)) { @@ -114,10 +114,7 @@ export class FinalizeSto extends Procedure { }); if (!stoModule) { - throw new PolymathError({ - code: ErrorCode.ProcedureValidationError, - message: stoModuleErrorMessage, - }); + throw stoModuleError; } const { tokensSold, capPerTier } = await stoModule.getSTODetails(); From d283b30acb83c73715de649fe810e6a5e7b13da3 Mon Sep 17 00:00:00 2001 From: Jeremias Diaz Date: Tue, 7 Jan 2020 22:49:50 -0300 Subject: [PATCH 30/33] test: adapt tests to proper use of canTransfer --- src/procedures/FinalizeSto.ts | 17 ++++++++++------- src/procedures/__tests__/FinalizeSto.ts | 15 +++++++++++---- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/procedures/FinalizeSto.ts b/src/procedures/FinalizeSto.ts index 4b7a896..b76492b 100644 --- a/src/procedures/FinalizeSto.ts +++ b/src/procedures/FinalizeSto.ts @@ -64,17 +64,18 @@ export class FinalizeSto extends Procedure { if (statusCode !== TransferStatusCode.TransferSuccess) { throw new PolymathError({ code: ErrorCode.ProcedureValidationError, - message: `Treasury wallet "${to}" is not cleared to - receive the remaining ${amount} "${symbol}" tokens from "${fromAddress}". - Please review transfer restrictions regarding this wallet address before - attempting to finalize the STO. Possible reason: "${reasonCode}"`, + message: `Treasury wallet "${to}" is not cleared to \ +receive the remaining ${amount} "${symbol}" tokens from "${fromAddress}". \ +Please review transfer restrictions regarding this wallet address before attempting \ +to finalize the STO. Possible reason: "${reasonCode}"`, }); } } public async prepareTransactions() { - const { stoAddress, stoType, symbol } = this.args; - const { contractWrappers, factories } = this.context; + const { context, args } = this; + const { stoAddress, stoType, symbol } = args; + const { contractWrappers, factories, currentWallet } = context; /* * Validation @@ -167,13 +168,15 @@ export class FinalizeSto extends Procedure { }); } + const address = await currentWallet.address(); + const { statusCode, reasonCode } = await securityToken.canTransfer({ to: treasuryWallet, value: remainingTokens, }); this.checkTransferStatus( statusCode, - await this.context.currentWallet.address(), + address, symbol, treasuryWallet, reasonCode, diff --git a/src/procedures/__tests__/FinalizeSto.ts b/src/procedures/__tests__/FinalizeSto.ts index 5e950c8..b33ec26 100644 --- a/src/procedures/__tests__/FinalizeSto.ts +++ b/src/procedures/__tests__/FinalizeSto.ts @@ -39,6 +39,7 @@ const tieredParams: FinalizeStoProcedureArgs = { const invalidSto = 'InvalidSto'; const treasuryWallet = '0x1111111111111111111111111111111111111111'; +const currentWallet = '0x2222222222222222222222222222222222222222'; const amountOfTokens = new BigNumber(1); describe('FinalizeSto', () => { @@ -74,6 +75,9 @@ describe('FinalizeSto', () => { ); contextMock.set('contractWrappers', wrappersMock.getMockInstance()); + contextMock.set('currentWallet', { + address: () => Promise.resolve(currentWallet), + }); wrappersMock.set('tokenFactory', tokenFactoryMock.getMockInstance()); wrappersMock.set('moduleFactory', moduleWrapperFactoryMock.getMockInstance()); @@ -274,16 +278,19 @@ describe('FinalizeSto', () => { ); }); - // This test will change once canTransfer is refactored in project - test('should throw an error if can transfer returns null', async () => { - securityTokenMock.mock('canTransfer', Promise.resolve(undefined)); + test('should throw an error if can transfer returns a status different from success', async () => { + const reasonCode = 'Failed'; + securityTokenMock.mock( + 'canTransfer', + Promise.resolve({ statusCode: TransferStatusCode.TransferFailure, reasonCode }) + ); await expect(target.prepareTransactions()).rejects.toThrow( new PolymathError({ code: ErrorCode.InvalidAddress, message: `Treasury wallet "${treasuryWallet}" is not cleared to receive the remaining ${amountOfTokens} "${ simpleParams.symbol - }" tokens. Please review transfer restrictions regarding this wallet address before attempting to finalize the STO`, + }" tokens from "${currentWallet}". Please review transfer restrictions regarding this wallet address before attempting to finalize the STO. Possible reason: "${reasonCode}"`, }) ); }); From dd183181d01dc80fd93b5aac6bbccd20318274d0 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 8 Jan 2020 01:55:51 +0000 Subject: [PATCH 31/33] chore(release): 2.0.1-beta.88 [skip ci] ## [2.0.1-beta.88](https://github.com/PolymathNetwork/polymath-sdk/compare/v2.0.1-beta.87@beta...v2.0.1-beta.88@beta) (2020-01-08) ### Bug Fixes * make error a const ([0410e69](https://github.com/PolymathNetwork/polymath-sdk/commit/0410e69)) * review comments and feedback ([daa2c2c](https://github.com/PolymathNetwork/polymath-sdk/commit/daa2c2c)) * use the new check transfer status pattern to warn about failure ([7c0f734](https://github.com/PolymathNetwork/polymath-sdk/commit/7c0f734)) * yarn issues ([da08c79](https://github.com/PolymathNetwork/polymath-sdk/commit/da08c79)) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 918fedf..b451fd0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@polymathnetwork/sdk", - "version": "2.0.1-beta.87", + "version": "2.0.1-beta.88", "description": "A Javascript SDK for interacting with the Polymath network for the browser and Node.js", "bugs": { "url": "https://github.com/PolymathNetwork/polymath-sdk/issues" From c4d178cf368e62edf57c63e23d4295e5d783d135 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 8 Jan 2020 04:45:50 +0000 Subject: [PATCH 32/33] chore(release): 2.0.1-beta.89 [skip ci] ## [2.0.1-beta.89](https://github.com/PolymathNetwork/polymath-sdk/compare/v2.0.1-beta.88@beta...v2.0.1-beta.89@beta) (2020-01-08) ### Bug Fixes * descriptions and unnecessary code in two procedures ([59ba9e5](https://github.com/PolymathNetwork/polymath-sdk/commit/59ba9e5)) * remove redundant code from toggle pause sto ([80edb84](https://github.com/PolymathNetwork/polymath-sdk/commit/80edb84)) * remove unnecessary code ([994c51d](https://github.com/PolymathNetwork/polymath-sdk/commit/994c51d)) * remove unnecessary import ([3364ea8](https://github.com/PolymathNetwork/polymath-sdk/commit/3364ea8)) * review comments, unnecessary code and internal func ([07ad557](https://github.com/PolymathNetwork/polymath-sdk/commit/07ad557)) * small fixes before review ([ac63126](https://github.com/PolymathNetwork/polymath-sdk/commit/ac63126)) * sto module error issue ([a1a3e53](https://github.com/PolymathNetwork/polymath-sdk/commit/a1a3e53)) * wrong factory type ([7bd8630](https://github.com/PolymathNetwork/polymath-sdk/commit/7bd8630)) * yarn issues ([c0a5392](https://github.com/PolymathNetwork/polymath-sdk/commit/c0a5392)) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b451fd0..4639a1e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@polymathnetwork/sdk", - "version": "2.0.1-beta.88", + "version": "2.0.1-beta.89", "description": "A Javascript SDK for interacting with the Polymath network for the browser and Node.js", "bugs": { "url": "https://github.com/PolymathNetwork/polymath-sdk/issues" From 92e16de05c6f0506b7d545b1ac81009117fa9ee3 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 8 Jan 2020 14:22:28 +0000 Subject: [PATCH 33/33] chore(release): 2.0.1-beta.90 [skip ci] ## [2.0.1-beta.90](https://github.com/PolymathNetwork/polymath-sdk/compare/v2.0.1-beta.89@beta...v2.0.1-beta.90@beta) (2020-01-08) ### Bug Fixes * feedback improvements ([25bca45](https://github.com/PolymathNetwork/polymath-sdk/commit/25bca45)) * travis CI fix ([bfaeff9](https://github.com/PolymathNetwork/polymath-sdk/commit/bfaeff9)) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4639a1e..e53e1a0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@polymathnetwork/sdk", - "version": "2.0.1-beta.89", + "version": "2.0.1-beta.90", "description": "A Javascript SDK for interacting with the Polymath network for the browser and Node.js", "bugs": { "url": "https://github.com/PolymathNetwork/polymath-sdk/issues"