Skip to content

Commit

Permalink
feat: fetch fees automatically
Browse files Browse the repository at this point in the history
  • Loading branch information
monitz87 committed May 13, 2020
1 parent 81bb459 commit e8d8c3e
Show file tree
Hide file tree
Showing 20 changed files with 354 additions and 245 deletions.
2 changes: 2 additions & 0 deletions package.json
Expand Up @@ -26,6 +26,7 @@
"@commitlint/cli": "^7.6.1",
"@commitlint/config-conventional": "^7.6.0",
"@polkadot/typegen": "1.12.0-beta.5",
"@types/bluebird": "^3.5.30",
"@types/jest": "^23.3.10",
"@types/json-stable-stringify": "^1.0.32",
"@types/lodash": "^4.14.149",
Expand Down Expand Up @@ -82,6 +83,7 @@
"@polkadot/util-crypto": "2.8.1",
"@types/bignumber.js": "^5.0.0",
"bignumber.js": "^9.0.0",
"bluebird": "^3.7.2",
"json-stable-stringify": "^1.0.1",
"lodash": "^4.17.15"
}
Expand Down
1 change: 0 additions & 1 deletion src/api/entities/Identity/index.ts
Expand Up @@ -109,7 +109,6 @@ export class Identity extends Entity<UniqueIdentifiers> {

throw new PolymeshError({
code: ErrorCode.ValidationError,
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
message: `Unrecognized role "${JSON.stringify(role)}"`,
});
}
Expand Down
50 changes: 2 additions & 48 deletions src/api/procedures/__tests__/createSecurityToken.ts
Expand Up @@ -8,7 +8,6 @@ import {
Document,
FundingRoundName,
IdentifierType,
PosRatio,
Ticker,
TokenName,
} from 'polymesh-types/types';
Expand Down Expand Up @@ -56,8 +55,6 @@ describe('createSecurityToken procedure', () => {
let stringToAssetIdentifierStub: sinon.SinonStub<[string, Context], AssetIdentifier>;
let stringToFundingRoundNameStub: sinon.SinonStub<[string, Context], FundingRoundName>;
let tokenDocumentToDocumentStub: sinon.SinonStub<[TokenDocument, Context], Document>;
let posRatioToBigNumberStub: sinon.SinonStub<[PosRatio], BigNumber>;
let balanceToBigNumberStub: sinon.SinonStub<[Balance], BigNumber>;
let ticker: string;
let name: string;
let totalSupply: BigNumber;
Expand All @@ -75,12 +72,6 @@ describe('createSecurityToken procedure', () => {
let rawFundingRound: FundingRoundName;
let rawDocuments: Document[];
let args: Params;
let fee: number;
let rawPosRatio: PosRatio;
let rawFee: Balance;
let numerator: number;
let denominator: number;
let posRatioToBigNumberResult: BigNumber;

beforeAll(() => {
polkadotMockUtils.initMocks({ contextOptions: { balance: new BigNumber(1000) } });
Expand All @@ -98,10 +89,6 @@ describe('createSecurityToken procedure', () => {
stringToAssetIdentifierStub = sinon.stub(utilsModule, 'stringToAssetIdentifier');
stringToFundingRoundNameStub = sinon.stub(utilsModule, 'stringToFundingRoundName');
tokenDocumentToDocumentStub = sinon.stub(utilsModule, 'tokenDocumentToDocument');
posRatioToBigNumberStub = sinon.stub(utilsModule, 'posRatioToBigNumber');
balanceToBigNumberStub = sinon.stub(utilsModule, 'balanceToBigNumber');
numerator = 5;
denominator = 2;
ticker = 'someTicker';
name = 'someName';
totalSupply = new BigNumber(100);
Expand Down Expand Up @@ -141,7 +128,6 @@ describe('createSecurityToken procedure', () => {
})
);
rawFundingRound = polkadotMockUtils.createMockFundingRoundName(fundingRound);
rawPosRatio = polkadotMockUtils.createMockPosRatio(numerator, denominator);
args = {
ticker,
name,
Expand All @@ -151,9 +137,6 @@ describe('createSecurityToken procedure', () => {
tokenIdentifiers,
fundingRound,
};
fee = 250;
rawFee = polkadotMockUtils.createMockBalance(fee);
posRatioToBigNumberResult = new BigNumber(numerator).dividedBy(new BigNumber(denominator));
});

let addTransactionStub: sinon.SinonStub;
Expand All @@ -173,12 +156,6 @@ describe('createSecurityToken procedure', () => {
beforeEach(() => {
addTransactionStub = procedureMockUtils.getAddTransactionStub();

polkadotMockUtils.createQueryStub('protocolFee', 'coefficient', {
returnValue: rawPosRatio,
});
polkadotMockUtils.createQueryStub('protocolFee', 'baseFees', {
returnValue: rawFee,
});
polkadotMockUtils.createQueryStub('asset', 'tickerConfig', {
returnValue: polkadotMockUtils.createMockTickerRegistrationConfig(),
});
Expand All @@ -200,10 +177,6 @@ describe('createSecurityToken procedure', () => {
.returns(rawIdentifiers[0][1]);
stringToFundingRoundNameStub.withArgs(fundingRound, mockContext).returns(rawFundingRound);
tokenDocumentToDocumentStub.withArgs(documents[0], mockContext).returns(rawDocuments[0]);

posRatioToBigNumberStub.withArgs(rawPosRatio).returns(posRatioToBigNumberResult);

balanceToBigNumberStub.withArgs(rawFee).returns(new BigNumber(fee));
});

afterEach(() => {
Expand Down Expand Up @@ -246,21 +219,6 @@ describe('createSecurityToken procedure', () => {
);
});

test("should throw an error if the signing account doesn't have enough balance", () => {
const fakeValue = 600000000;
const fakeBalance = polkadotMockUtils.createMockBalance(fakeValue);
polkadotMockUtils.createQueryStub('protocolFee', 'baseFees', {
returnValue: fakeBalance,
});
balanceToBigNumberStub.withArgs(fakeBalance).returns(new BigNumber(fakeValue));
const proc = procedureMockUtils.getInstance<Params, SecurityToken>();
proc.context = mockContext;

return expect(prepareCreateSecurityToken.call(proc, args)).rejects.toThrow(
'Not enough POLYX balance to pay for token creation'
);
});

test('should add a token creation transaction to the queue', async () => {
const proc = procedureMockUtils.getInstance<Params, SecurityToken>();
proc.context = mockContext;
Expand All @@ -270,9 +228,7 @@ describe('createSecurityToken procedure', () => {
sinon.assert.calledWith(
addTransactionStub.firstCall,
transaction,
sinon.match({
fee: new BigNumber(fee).multipliedBy(posRatioToBigNumberResult),
}),
{},
rawName,
rawTicker,
rawTotalSupply,
Expand All @@ -292,9 +248,7 @@ describe('createSecurityToken procedure', () => {
sinon.assert.calledWith(
addTransactionStub.secondCall,
transaction,
sinon.match({
fee: new BigNumber(fee).multipliedBy(posRatioToBigNumberResult),
}),
{},
rawName,
rawTicker,
rawTotalSupply,
Expand Down
88 changes: 18 additions & 70 deletions src/api/procedures/__tests__/reserveTicker.ts
@@ -1,7 +1,6 @@
import { Balance } from '@polkadot/types/interfaces';
import { ISubmittableResult } from '@polkadot/types/types';
import BigNumber from 'bignumber.js';
import { PosRatio, Ticker } from 'polymesh-types/types';
import { Ticker } from 'polymesh-types/types';
import sinon from 'sinon';

import { TickerReservation } from '~/api/entities';
Expand Down Expand Up @@ -29,38 +28,22 @@ jest.mock(
describe('reserveTicker procedure', () => {
let mockContext: Mocked<Context>;
let stringToTickerStub: sinon.SinonStub<[string, Context], Ticker>;
let posRatioToBigNumberStub: sinon.SinonStub<[PosRatio], BigNumber>;
let balanceToBigNumberStub: sinon.SinonStub<[Balance], BigNumber>;
let ticker: string;
let rawTicker: Ticker;
let args: ReserveTickerParams;
let fee: number;
let reservation: PostTransactionValue<TickerReservation>;
let rawPosRatio: PosRatio;
let rawFee: Balance;
let numerator: number;
let denominator: number;
let posRatioToBigNumberResult: BigNumber;

beforeAll(() => {
polkadotMockUtils.initMocks({ contextOptions: { balance: new BigNumber(1000) } });
procedureMockUtils.initMocks();
entityMockUtils.initMocks({ identityOptions: { did: 'someOtherDid' } });
stringToTickerStub = sinon.stub(utilsModule, 'stringToTicker');
posRatioToBigNumberStub = sinon.stub(utilsModule, 'posRatioToBigNumber');
balanceToBigNumberStub = sinon.stub(utilsModule, 'balanceToBigNumber');
ticker = 'someTicker';
rawTicker = polkadotMockUtils.createMockTicker(ticker);
args = {
ticker,
};
fee = 250;
numerator = 7;
denominator = 3;
reservation = ('reservation' as unknown) as PostTransactionValue<TickerReservation>;
rawPosRatio = polkadotMockUtils.createMockPosRatio(numerator, denominator);
rawFee = polkadotMockUtils.createMockBalance(fee);
posRatioToBigNumberResult = new BigNumber(numerator).dividedBy(new BigNumber(denominator));
});

let addTransactionStub: sinon.SinonStub;
Expand All @@ -76,12 +59,6 @@ describe('reserveTicker procedure', () => {
status: TickerReservationStatus.Free,
});

polkadotMockUtils.createQueryStub('protocolFee', 'coefficient', {
returnValue: rawPosRatio,
});
polkadotMockUtils.createQueryStub('protocolFee', 'baseFees', {
returnValue: rawFee,
});
polkadotMockUtils.createQueryStub('asset', 'tickerConfig', {
returnValue: polkadotMockUtils.createMockTickerRegistrationConfig(),
});
Expand All @@ -91,14 +68,6 @@ describe('reserveTicker procedure', () => {
mockContext = polkadotMockUtils.getContextInstance();

stringToTickerStub.withArgs(ticker, mockContext).returns(rawTicker);

posRatioToBigNumberStub
.withArgs(rawPosRatio)
.returns(new BigNumber(numerator).dividedBy(new BigNumber(denominator)));

posRatioToBigNumberStub.withArgs(rawPosRatio).returns(posRatioToBigNumberResult);

balanceToBigNumberStub.withArgs(rawFee).returns(new BigNumber(fee));
});

afterEach(() => {
Expand Down Expand Up @@ -176,21 +145,6 @@ describe('reserveTicker procedure', () => {
);
});

test("should throw an error if the signing account doesn't have enough balance", () => {
const fakeValue = 600000000;
const fakeBalance = polkadotMockUtils.createMockBalance(fakeValue);
polkadotMockUtils.createQueryStub('protocolFee', 'baseFees', {
returnValue: fakeBalance,
});
balanceToBigNumberStub.withArgs(fakeBalance).returns(new BigNumber(fakeValue));
const proc = procedureMockUtils.getInstance<ReserveTickerParams, TickerReservation>();
proc.context = mockContext;

return expect(prepareReserveTicker.call(proc, args)).rejects.toThrow(
'Not enough POLYX balance to pay for ticker reservation'
);
});

test('should throw an error if extendPeriod property is set to true and the ticker has not been reserved or the reservation has expired', async () => {
const expiryDate = new Date(2019, 1, 1);
entityMockUtils.getTickerReservationDetailsStub().resolves({
Expand All @@ -206,44 +160,38 @@ describe('reserveTicker procedure', () => {
);
});

test("should throw an error if extendPeriod property is set to true and the signing account doesn't have enough balance", () => {
const expiryDate = new Date(new Date().getTime() + 1000);
const fakeValue = 600000000;
const fakeBalance = polkadotMockUtils.createMockBalance(fakeValue);
entityMockUtils.getTickerReservationDetailsStub().resolves({
owner: entityMockUtils.getIdentityInstance({ did: 'someDid' }),
expiryDate,
status: TickerReservationStatus.Reserved,
});
polkadotMockUtils.createQueryStub('protocolFee', 'baseFees', {
returnValue: fakeBalance,
});
balanceToBigNumberStub.withArgs(fakeBalance).returns(new BigNumber(fakeValue));

test('should add a register ticker transaction to the queue', async () => {
const proc = procedureMockUtils.getInstance<ReserveTickerParams, TickerReservation>();
proc.context = mockContext;

return expect(prepareReserveTicker.call(proc, { ...args, extendPeriod: true })).rejects.toThrow(
'Not enough POLYX balance to pay for ticker period extension'
let result = await prepareReserveTicker.call(proc, args);

sinon.assert.calledWith(
addTransactionStub,
transaction,
sinon.match({
resolvers: sinon.match.array,
}),
rawTicker
);
});
expect(result).toBe(reservation);

test('should add a register ticker transaction to the queue', async () => {
const proc = procedureMockUtils.getInstance<ReserveTickerParams, TickerReservation>();
proc.context = mockContext;
entityMockUtils.getTickerReservationDetailsStub().resolves({
owner: entityMockUtils.getIdentityInstance(),
expriy: new Date(3000, 12, 12),
status: TickerReservationStatus.Reserved,
});

const result = await prepareReserveTicker.call(proc, args);
result = await prepareReserveTicker.call(proc, { ...args, extendPeriod: true });

sinon.assert.calledWith(
addTransactionStub,
transaction,
sinon.match({
fee: new BigNumber(fee).multipliedBy(posRatioToBigNumberResult),
resolvers: sinon.match.array,
}),
rawTicker
);
expect(result).toBe(reservation);
});
});

Expand Down
28 changes: 3 additions & 25 deletions src/api/procedures/createSecurityToken.ts
Expand Up @@ -5,7 +5,6 @@ import { SecurityToken, TickerReservation } from '~/api/entities';
import { PolymeshError, Procedure } from '~/base';
import {
ErrorCode,
ProtocolOp,
Role,
RoleType,
TickerReservationStatus,
Expand All @@ -14,10 +13,8 @@ import {
TokenType,
} from '~/types';
import {
balanceToBigNumber,
booleanToBool,
numberToBalance,
posRatioToBigNumber,
stringToAssetIdentifier,
stringToFundingRoundName,
stringToTicker,
Expand Down Expand Up @@ -50,7 +47,7 @@ export async function prepareCreateSecurityToken(
): Promise<SecurityToken> {
const {
context: {
polymeshApi: { query, tx },
polymeshApi: { tx },
},
context,
} = this;
Expand All @@ -67,13 +64,7 @@ export async function prepareCreateSecurityToken(

const reservation = new TickerReservation({ ticker }, context);

// TODO: queryMulti
const [rawCoefficient, rawCreateTokenFee, balance, { status }] = await Promise.all([
query.protocolFee.coefficient(),
query.protocolFee.baseFees(ProtocolOp.AssetCreateToken),
context.accountBalance(),
reservation.details(),
]);
const { status } = await reservation.details();

if (status === TickerReservationStatus.TokenCreated) {
throw new PolymeshError({
Expand All @@ -89,17 +80,6 @@ export async function prepareCreateSecurityToken(
});
}

const ratio = posRatioToBigNumber(rawCoefficient);
const createTokenFee = balanceToBigNumber(rawCreateTokenFee);
const fee = createTokenFee.multipliedBy(ratio);

if (balance.lt(fee)) {
throw new PolymeshError({
code: ErrorCode.ValidationError,
message: 'Not enough POLYX balance to pay for token creation',
});
}

const rawTicker = stringToTicker(ticker, context);
const rawTotalSupply = numberToBalance(totalSupply, context);
const rawName = stringToTokenName(name, context);
Expand All @@ -117,9 +97,7 @@ export async function prepareCreateSecurityToken(

this.addTransaction(
tx.asset.createAsset,
{
fee,
},
{},
rawName,
rawTicker,
rawTotalSupply,
Expand Down

0 comments on commit e8d8c3e

Please sign in to comment.