Skip to content

Commit

Permalink
feat: 🎸 Add endpoints to link, unlink ticker
Browse files Browse the repository at this point in the history
  • Loading branch information
prashantasdeveloper committed Nov 14, 2024
1 parent 953f835 commit bbfbda8
Show file tree
Hide file tree
Showing 9 changed files with 210 additions and 85 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
"@polymeshassociation/fireblocks-signing-manager": "^2.5.0",
"@polymeshassociation/hashicorp-vault-signing-manager": "^3.4.0",
"@polymeshassociation/local-signing-manager": "^3.3.0",
"@polymeshassociation/polymesh-sdk": "^26.2.0-alpha.1",
"@polymeshassociation/polymesh-sdk": "^26.2.0-alpha.4",
"@polymeshassociation/signing-manager-types": "^3.2.0",
"class-transformer": "0.5.1",
"class-validator": "^0.14.0",
Expand Down
21 changes: 21 additions & 0 deletions src/assets/assets.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -440,4 +440,25 @@ describe('AssetsController', () => {
});
});
});

describe('linkTicker', () => {
it('should call the service and return the results', async () => {
const ticker = 'TICKER';
mockAssetsService.linkTickerToAsset.mockResolvedValue(txResult);

const result = await controller.linkTicker({ asset: assetId }, { signer, ticker });
expect(result).toEqual(processedTxResult);
expect(mockAssetsService.linkTickerToAsset).toHaveBeenCalledWith(assetId, { signer, ticker });
});
});

describe('unlinkTicker', () => {
it('should call the service and return the results', async () => {
mockAssetsService.unlinkTickerFromAsset.mockResolvedValue(txResult);

const result = await controller.unlinkTicker({ asset: assetId }, { signer });
expect(result).toEqual(processedTxResult);
expect(mockAssetsService.unlinkTickerFromAsset).toHaveBeenCalledWith(assetId, { signer });
});
});
});
44 changes: 44 additions & 0 deletions src/assets/assets.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { AssetParamsDto } from '~/assets/dto/asset-params.dto';
import { ControllerTransferDto } from '~/assets/dto/controller-transfer.dto';
import { CreateAssetDto } from '~/assets/dto/create-asset.dto';
import { IssueDto } from '~/assets/dto/issue.dto';
import { LinkTickerDto } from '~/assets/dto/link-ticker.dto';
import { RedeemTokensDto } from '~/assets/dto/redeem-tokens.dto';
import { RequiredMediatorsDto } from '~/assets/dto/required-mediators.dto';
import { SetAssetDocumentsDto } from '~/assets/dto/set-asset-documents.dto';
Expand Down Expand Up @@ -583,4 +584,47 @@ export class AssetsController {

return handleServiceResult(result);
}

@ApiOperation({
summary: 'Link a ticker to an Asset',
description: 'This endpoint allows linking a ticker to an existing Asset',
})
@ApiTransactionResponse({
description: 'Details about the transaction',
type: TransactionQueueModel,
})
@ApiNotFoundResponse({
description: 'The Asset does not exist',
})
@ApiUnprocessableEntityResponse({
description: 'Ticker is already linked with another asset',
})
@Post(':asset/link-ticker')
public async linkTicker(
@Param() { asset }: AssetParamsDto,
@Body() params: LinkTickerDto
): Promise<TransactionResponseModel> {
const result = await this.assetsService.linkTickerToAsset(asset, params);
return handleServiceResult(result);
}

@ApiOperation({
summary: 'Unlink a ticker from an Asset',
description: 'This endpoint allows unlinking a ticker from an existing Asset',
})
@ApiTransactionResponse({
description: 'Details about the transaction',
type: TransactionQueueModel,
})
@ApiNotFoundResponse({
description: 'The Asset does not exist',
})
@Post(':asset/unlink-ticker')
public async unlinkTicker(
@Param() { asset }: AssetParamsDto,
@Body() params: TransactionBaseDto
): Promise<TransactionResponseModel> {
const result = await this.assetsService.unlinkTickerFromAsset(asset, params);
return handleServiceResult(result);
}
}
64 changes: 63 additions & 1 deletion src/assets/assets.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {
import { mockTransactionsProvider, MockTransactionsService } from '~/test-utils/service-mocks';
import * as transactionsUtilModule from '~/transactions/transactions.util';

const { did, signer } = testValues;
const { did, signer, assetId } = testValues;

jest.mock('@polymeshassociation/polymesh-sdk/utils', () => ({
...jest.requireActual('@polymeshassociation/polymesh-sdk/utils'),
Expand Down Expand Up @@ -704,4 +704,66 @@ describe('AssetsService', () => {
);
});
});

describe('linkTickerToAsset', () => {
it('should link the given ticker', async () => {
const transaction = {
blockHash: '0x1',
txHash: '0x2',
blockNumber: new BigNumber(1),
tag: TxTags.asset.LinkTickerToAssetId,
};
const findSpy = jest.spyOn(service, 'findOne');

const mockTransaction = new MockTransaction(transaction);
const mockAsset = new MockAsset();
mockTransactionsService.submit.mockResolvedValue({ transactions: [mockTransaction] });
// eslint-disable-next-line @typescript-eslint/no-explicit-any
findSpy.mockResolvedValue(mockAsset as any);

const result = await service.linkTickerToAsset(assetId, { signer, ticker: 'TICKER' });
expect(result).toEqual({
result: undefined,
transactions: [mockTransaction],
});

expect(mockTransactionsService.submit).toHaveBeenCalledWith(
mockAsset.linkTicker,
{
ticker: 'TICKER',
},
expect.objectContaining({ signer })
);
});
});

describe('unlinkTickerFromAsset', () => {
it('should unlink the ticker from the asset', async () => {
const transaction = {
blockHash: '0x1',
txHash: '0x2',
blockNumber: new BigNumber(1),
tag: TxTags.asset.UnlinkTickerFromAssetId,
};
const findSpy = jest.spyOn(service, 'findOne');

const mockTransaction = new MockTransaction(transaction);
const mockAsset = new MockAsset();
mockTransactionsService.submit.mockResolvedValue({ transactions: [mockTransaction] });
// eslint-disable-next-line @typescript-eslint/no-explicit-any
findSpy.mockResolvedValue(mockAsset as any);

const result = await service.unlinkTickerFromAsset(assetId, { signer });
expect(result).toEqual({
result: undefined,
transactions: [mockTransaction],
});

expect(mockTransactionsService.submit).toHaveBeenCalledWith(
mockAsset.unlinkTicker,
{},
expect.objectContaining({ signer })
);
});
});
});
84 changes: 54 additions & 30 deletions src/assets/assets.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
import { ControllerTransferDto } from '~/assets/dto/controller-transfer.dto';
import { CreateAssetDto } from '~/assets/dto/create-asset.dto';
import { IssueDto } from '~/assets/dto/issue.dto';
import { LinkTickerDto } from '~/assets/dto/link-ticker.dto';
import { RedeemTokensDto } from '~/assets/dto/redeem-tokens.dto';
import { RequiredMediatorsDto } from '~/assets/dto/required-mediators.dto';
import { SetAssetDocumentsDto } from '~/assets/dto/set-asset-documents.dto';
Expand Down Expand Up @@ -75,27 +76,27 @@ export class AssetsService {
}

public async findHolders(
ticker: string,
assetInput: string,
size: BigNumber,
start?: string
): Promise<ResultSet<IdentityBalance>> {
const asset = await this.findFungible(ticker);
const asset = await this.findFungible(assetInput);
return asset.assetHolders.get({ size, start });
}

public async findDocuments(
ticker: string,
assetInput: string,
size: BigNumber,
start?: string
): Promise<ResultSet<AssetDocument>> {
const asset = await this.findOne(ticker);
const asset = await this.findOne(assetInput);
return asset.documents.get({ size, start });
}

public async setDocuments(ticker: string, params: SetAssetDocumentsDto): ServiceReturn<void> {
public async setDocuments(assetInput: string, params: SetAssetDocumentsDto): ServiceReturn<void> {
const {
documents: { set },
} = await this.findOne(ticker);
} = await this.findOne(assetInput);
const { options, args } = extractTxOptions(params);

return this.transactionsService.submit(set, args, options);
Expand All @@ -108,27 +109,27 @@ export class AssetsService {
return this.transactionsService.submit(createAsset, args, options);
}

public async issue(ticker: string, params: IssueDto): ServiceReturn<FungibleAsset> {
public async issue(assetInput: string, params: IssueDto): ServiceReturn<FungibleAsset> {
const { options, args } = extractTxOptions(params);
const asset = await this.findFungible(ticker);
const asset = await this.findFungible(assetInput);

return this.transactionsService.submit(asset.issuance.issue, args, options);
}

public async transferOwnership(
ticker: string,
assetInput: string,
params: TransferOwnershipDto
): ServiceReturn<AuthorizationRequest> {
const { options, args } = extractTxOptions(params);

const { transferOwnership } = await this.findOne(ticker);
const { transferOwnership } = await this.findOne(assetInput);
return this.transactionsService.submit(transferOwnership, args, options);
}

public async redeem(ticker: string, params: RedeemTokensDto): ServiceReturn<void> {
public async redeem(assetInput: string, params: RedeemTokensDto): ServiceReturn<void> {
const { options, args } = extractTxOptions(params);

const { redeem } = await this.findFungible(ticker);
const { redeem } = await this.findFungible(assetInput);

return this.transactionsService.submit(
redeem,
Expand All @@ -137,32 +138,35 @@ export class AssetsService {
);
}

public async freeze(ticker: string, transactionBaseDto: TransactionBaseDto): ServiceReturn<void> {
public async freeze(
assetInput: string,
transactionBaseDto: TransactionBaseDto
): ServiceReturn<void> {
const { options } = extractTxOptions(transactionBaseDto);
const asset = await this.findOne(ticker);
const asset = await this.findOne(assetInput);

return this.transactionsService.submit(asset.freeze, {}, options);
}

public async unfreeze(
ticker: string,
assetInput: string,
transactionBaseDto: TransactionBaseDto
): ServiceReturn<void> {
const { options } = extractTxOptions(transactionBaseDto);
const asset = await this.findOne(ticker);
const asset = await this.findOne(assetInput);

return this.transactionsService.submit(asset.unfreeze, {}, options);
}

public async controllerTransfer(
ticker: string,
assetInput: string,
params: ControllerTransferDto
): ServiceReturn<void> {
const {
options,
args: { origin, amount },
} = extractTxOptions(params);
const { controllerTransfer } = await this.findFungible(ticker);
const { controllerTransfer } = await this.findFungible(assetInput);

return this.transactionsService.submit(
controllerTransfer,
Expand All @@ -171,61 +175,81 @@ export class AssetsService {
);
}

public async getOperationHistory(ticker: string): Promise<HistoricAgentOperation[]> {
const asset = await this.findFungible(ticker);
public async getOperationHistory(assetInput: string): Promise<HistoricAgentOperation[]> {
const asset = await this.findFungible(assetInput);
return asset.getOperationHistory();
}

public async getRequiredMediators(ticker: string): Promise<Identity[]> {
const asset = await this.findOne(ticker);
public async getRequiredMediators(assetInput: string): Promise<Identity[]> {
const asset = await this.findOne(assetInput);
return asset.getRequiredMediators().catch(error => {
throw handleSdkError(error);
});
}

public async addRequiredMediators(
ticker: string,
assetInput: string,
params: RequiredMediatorsDto
): ServiceReturn<void> {
const {
options,
args: { mediators },
} = extractTxOptions(params);
const { addRequiredMediators } = await this.findOne(ticker);
const { addRequiredMediators } = await this.findOne(assetInput);

return this.transactionsService.submit(addRequiredMediators, { mediators }, options);
}

public async removeRequiredMediators(
ticker: string,
assetInput: string,
params: RequiredMediatorsDto
): ServiceReturn<void> {
const {
options,
args: { mediators },
} = extractTxOptions(params);
const { removeRequiredMediators } = await this.findOne(ticker);
const { removeRequiredMediators } = await this.findOne(assetInput);

return this.transactionsService.submit(removeRequiredMediators, { mediators }, options);
}

public async preApprove(ticker: string, params: TransactionBaseDto): ServiceReturn<void> {
public async preApprove(assetInput: string, params: TransactionBaseDto): ServiceReturn<void> {
const { options } = extractTxOptions(params);

const {
settlements: { preApprove },
} = await this.findOne(ticker);
} = await this.findOne(assetInput);

return this.transactionsService.submit(preApprove, {}, options);
}

public async removePreApproval(ticker: string, params: TransactionBaseDto): ServiceReturn<void> {
public async removePreApproval(
assetInput: string,
params: TransactionBaseDto
): ServiceReturn<void> {
const { options } = extractTxOptions(params);

const {
settlements: { removePreApproval },
} = await this.findOne(ticker);
} = await this.findOne(assetInput);

return this.transactionsService.submit(removePreApproval, {}, options);
}

public async linkTickerToAsset(assetInput: string, params: LinkTickerDto): ServiceReturn<void> {
const { options, args } = extractTxOptions(params);

const { linkTicker } = await this.findOne(assetInput);
return this.transactionsService.submit(linkTicker, args, options);
}

public async unlinkTickerFromAsset(
assetInput: string,
params: TransactionBaseDto
): ServiceReturn<void> {
const { options } = extractTxOptions(params);

const { unlinkTicker } = await this.findOne(assetInput);
return this.transactionsService.submit(unlinkTicker, {}, options);
}
}
15 changes: 15 additions & 0 deletions src/assets/dto/link-ticker.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/* istanbul ignore file */
import { ApiProperty } from '@nestjs/swagger';

import { IsTicker } from '~/common/decorators/validation';
import { TransactionBaseDto } from '~/common/dto/transaction-base-dto';

export class LinkTickerDto extends TransactionBaseDto {
@ApiProperty({
description: 'Ticker to be linked with the Asset',
example: 'TICKER',
type: 'string',
})
@IsTicker()
readonly ticker: string;
}
Loading

0 comments on commit bbfbda8

Please sign in to comment.