Skip to content

Commit bbfbda8

Browse files
feat: 🎸 Add endpoints to link, unlink ticker
1 parent 953f835 commit bbfbda8

9 files changed

+210
-85
lines changed

‎package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
"@polymeshassociation/fireblocks-signing-manager": "^2.5.0",
5050
"@polymeshassociation/hashicorp-vault-signing-manager": "^3.4.0",
5151
"@polymeshassociation/local-signing-manager": "^3.3.0",
52-
"@polymeshassociation/polymesh-sdk": "^26.2.0-alpha.1",
52+
"@polymeshassociation/polymesh-sdk": "^26.2.0-alpha.4",
5353
"@polymeshassociation/signing-manager-types": "^3.2.0",
5454
"class-transformer": "0.5.1",
5555
"class-validator": "^0.14.0",

‎src/assets/assets.controller.spec.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,4 +440,25 @@ describe('AssetsController', () => {
440440
});
441441
});
442442
});
443+
444+
describe('linkTicker', () => {
445+
it('should call the service and return the results', async () => {
446+
const ticker = 'TICKER';
447+
mockAssetsService.linkTickerToAsset.mockResolvedValue(txResult);
448+
449+
const result = await controller.linkTicker({ asset: assetId }, { signer, ticker });
450+
expect(result).toEqual(processedTxResult);
451+
expect(mockAssetsService.linkTickerToAsset).toHaveBeenCalledWith(assetId, { signer, ticker });
452+
});
453+
});
454+
455+
describe('unlinkTicker', () => {
456+
it('should call the service and return the results', async () => {
457+
mockAssetsService.unlinkTickerFromAsset.mockResolvedValue(txResult);
458+
459+
const result = await controller.unlinkTicker({ asset: assetId }, { signer });
460+
expect(result).toEqual(processedTxResult);
461+
expect(mockAssetsService.unlinkTickerFromAsset).toHaveBeenCalledWith(assetId, { signer });
462+
});
463+
});
443464
});

‎src/assets/assets.controller.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { AssetParamsDto } from '~/assets/dto/asset-params.dto';
1818
import { ControllerTransferDto } from '~/assets/dto/controller-transfer.dto';
1919
import { CreateAssetDto } from '~/assets/dto/create-asset.dto';
2020
import { IssueDto } from '~/assets/dto/issue.dto';
21+
import { LinkTickerDto } from '~/assets/dto/link-ticker.dto';
2122
import { RedeemTokensDto } from '~/assets/dto/redeem-tokens.dto';
2223
import { RequiredMediatorsDto } from '~/assets/dto/required-mediators.dto';
2324
import { SetAssetDocumentsDto } from '~/assets/dto/set-asset-documents.dto';
@@ -583,4 +584,47 @@ export class AssetsController {
583584

584585
return handleServiceResult(result);
585586
}
587+
588+
@ApiOperation({
589+
summary: 'Link a ticker to an Asset',
590+
description: 'This endpoint allows linking a ticker to an existing Asset',
591+
})
592+
@ApiTransactionResponse({
593+
description: 'Details about the transaction',
594+
type: TransactionQueueModel,
595+
})
596+
@ApiNotFoundResponse({
597+
description: 'The Asset does not exist',
598+
})
599+
@ApiUnprocessableEntityResponse({
600+
description: 'Ticker is already linked with another asset',
601+
})
602+
@Post(':asset/link-ticker')
603+
public async linkTicker(
604+
@Param() { asset }: AssetParamsDto,
605+
@Body() params: LinkTickerDto
606+
): Promise<TransactionResponseModel> {
607+
const result = await this.assetsService.linkTickerToAsset(asset, params);
608+
return handleServiceResult(result);
609+
}
610+
611+
@ApiOperation({
612+
summary: 'Unlink a ticker from an Asset',
613+
description: 'This endpoint allows unlinking a ticker from an existing Asset',
614+
})
615+
@ApiTransactionResponse({
616+
description: 'Details about the transaction',
617+
type: TransactionQueueModel,
618+
})
619+
@ApiNotFoundResponse({
620+
description: 'The Asset does not exist',
621+
})
622+
@Post(':asset/unlink-ticker')
623+
public async unlinkTicker(
624+
@Param() { asset }: AssetParamsDto,
625+
@Body() params: TransactionBaseDto
626+
): Promise<TransactionResponseModel> {
627+
const result = await this.assetsService.unlinkTickerFromAsset(asset, params);
628+
return handleServiceResult(result);
629+
}
586630
}

‎src/assets/assets.service.spec.ts

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import {
2525
import { mockTransactionsProvider, MockTransactionsService } from '~/test-utils/service-mocks';
2626
import * as transactionsUtilModule from '~/transactions/transactions.util';
2727

28-
const { did, signer } = testValues;
28+
const { did, signer, assetId } = testValues;
2929

3030
jest.mock('@polymeshassociation/polymesh-sdk/utils', () => ({
3131
...jest.requireActual('@polymeshassociation/polymesh-sdk/utils'),
@@ -704,4 +704,66 @@ describe('AssetsService', () => {
704704
);
705705
});
706706
});
707+
708+
describe('linkTickerToAsset', () => {
709+
it('should link the given ticker', async () => {
710+
const transaction = {
711+
blockHash: '0x1',
712+
txHash: '0x2',
713+
blockNumber: new BigNumber(1),
714+
tag: TxTags.asset.LinkTickerToAssetId,
715+
};
716+
const findSpy = jest.spyOn(service, 'findOne');
717+
718+
const mockTransaction = new MockTransaction(transaction);
719+
const mockAsset = new MockAsset();
720+
mockTransactionsService.submit.mockResolvedValue({ transactions: [mockTransaction] });
721+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
722+
findSpy.mockResolvedValue(mockAsset as any);
723+
724+
const result = await service.linkTickerToAsset(assetId, { signer, ticker: 'TICKER' });
725+
expect(result).toEqual({
726+
result: undefined,
727+
transactions: [mockTransaction],
728+
});
729+
730+
expect(mockTransactionsService.submit).toHaveBeenCalledWith(
731+
mockAsset.linkTicker,
732+
{
733+
ticker: 'TICKER',
734+
},
735+
expect.objectContaining({ signer })
736+
);
737+
});
738+
});
739+
740+
describe('unlinkTickerFromAsset', () => {
741+
it('should unlink the ticker from the asset', async () => {
742+
const transaction = {
743+
blockHash: '0x1',
744+
txHash: '0x2',
745+
blockNumber: new BigNumber(1),
746+
tag: TxTags.asset.UnlinkTickerFromAssetId,
747+
};
748+
const findSpy = jest.spyOn(service, 'findOne');
749+
750+
const mockTransaction = new MockTransaction(transaction);
751+
const mockAsset = new MockAsset();
752+
mockTransactionsService.submit.mockResolvedValue({ transactions: [mockTransaction] });
753+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
754+
findSpy.mockResolvedValue(mockAsset as any);
755+
756+
const result = await service.unlinkTickerFromAsset(assetId, { signer });
757+
expect(result).toEqual({
758+
result: undefined,
759+
transactions: [mockTransaction],
760+
});
761+
762+
expect(mockTransactionsService.submit).toHaveBeenCalledWith(
763+
mockAsset.unlinkTicker,
764+
{},
765+
expect.objectContaining({ signer })
766+
);
767+
});
768+
});
707769
});

‎src/assets/assets.service.ts

Lines changed: 54 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
import { ControllerTransferDto } from '~/assets/dto/controller-transfer.dto';
1616
import { CreateAssetDto } from '~/assets/dto/create-asset.dto';
1717
import { IssueDto } from '~/assets/dto/issue.dto';
18+
import { LinkTickerDto } from '~/assets/dto/link-ticker.dto';
1819
import { RedeemTokensDto } from '~/assets/dto/redeem-tokens.dto';
1920
import { RequiredMediatorsDto } from '~/assets/dto/required-mediators.dto';
2021
import { SetAssetDocumentsDto } from '~/assets/dto/set-asset-documents.dto';
@@ -75,27 +76,27 @@ export class AssetsService {
7576
}
7677

7778
public async findHolders(
78-
ticker: string,
79+
assetInput: string,
7980
size: BigNumber,
8081
start?: string
8182
): Promise<ResultSet<IdentityBalance>> {
82-
const asset = await this.findFungible(ticker);
83+
const asset = await this.findFungible(assetInput);
8384
return asset.assetHolders.get({ size, start });
8485
}
8586

8687
public async findDocuments(
87-
ticker: string,
88+
assetInput: string,
8889
size: BigNumber,
8990
start?: string
9091
): Promise<ResultSet<AssetDocument>> {
91-
const asset = await this.findOne(ticker);
92+
const asset = await this.findOne(assetInput);
9293
return asset.documents.get({ size, start });
9394
}
9495

95-
public async setDocuments(ticker: string, params: SetAssetDocumentsDto): ServiceReturn<void> {
96+
public async setDocuments(assetInput: string, params: SetAssetDocumentsDto): ServiceReturn<void> {
9697
const {
9798
documents: { set },
98-
} = await this.findOne(ticker);
99+
} = await this.findOne(assetInput);
99100
const { options, args } = extractTxOptions(params);
100101

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

111-
public async issue(ticker: string, params: IssueDto): ServiceReturn<FungibleAsset> {
112+
public async issue(assetInput: string, params: IssueDto): ServiceReturn<FungibleAsset> {
112113
const { options, args } = extractTxOptions(params);
113-
const asset = await this.findFungible(ticker);
114+
const asset = await this.findFungible(assetInput);
114115

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

118119
public async transferOwnership(
119-
ticker: string,
120+
assetInput: string,
120121
params: TransferOwnershipDto
121122
): ServiceReturn<AuthorizationRequest> {
122123
const { options, args } = extractTxOptions(params);
123124

124-
const { transferOwnership } = await this.findOne(ticker);
125+
const { transferOwnership } = await this.findOne(assetInput);
125126
return this.transactionsService.submit(transferOwnership, args, options);
126127
}
127128

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

131-
const { redeem } = await this.findFungible(ticker);
132+
const { redeem } = await this.findFungible(assetInput);
132133

133134
return this.transactionsService.submit(
134135
redeem,
@@ -137,32 +138,35 @@ export class AssetsService {
137138
);
138139
}
139140

140-
public async freeze(ticker: string, transactionBaseDto: TransactionBaseDto): ServiceReturn<void> {
141+
public async freeze(
142+
assetInput: string,
143+
transactionBaseDto: TransactionBaseDto
144+
): ServiceReturn<void> {
141145
const { options } = extractTxOptions(transactionBaseDto);
142-
const asset = await this.findOne(ticker);
146+
const asset = await this.findOne(assetInput);
143147

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

147151
public async unfreeze(
148-
ticker: string,
152+
assetInput: string,
149153
transactionBaseDto: TransactionBaseDto
150154
): ServiceReturn<void> {
151155
const { options } = extractTxOptions(transactionBaseDto);
152-
const asset = await this.findOne(ticker);
156+
const asset = await this.findOne(assetInput);
153157

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

157161
public async controllerTransfer(
158-
ticker: string,
162+
assetInput: string,
159163
params: ControllerTransferDto
160164
): ServiceReturn<void> {
161165
const {
162166
options,
163167
args: { origin, amount },
164168
} = extractTxOptions(params);
165-
const { controllerTransfer } = await this.findFungible(ticker);
169+
const { controllerTransfer } = await this.findFungible(assetInput);
166170

167171
return this.transactionsService.submit(
168172
controllerTransfer,
@@ -171,61 +175,81 @@ export class AssetsService {
171175
);
172176
}
173177

174-
public async getOperationHistory(ticker: string): Promise<HistoricAgentOperation[]> {
175-
const asset = await this.findFungible(ticker);
178+
public async getOperationHistory(assetInput: string): Promise<HistoricAgentOperation[]> {
179+
const asset = await this.findFungible(assetInput);
176180
return asset.getOperationHistory();
177181
}
178182

179-
public async getRequiredMediators(ticker: string): Promise<Identity[]> {
180-
const asset = await this.findOne(ticker);
183+
public async getRequiredMediators(assetInput: string): Promise<Identity[]> {
184+
const asset = await this.findOne(assetInput);
181185
return asset.getRequiredMediators().catch(error => {
182186
throw handleSdkError(error);
183187
});
184188
}
185189

186190
public async addRequiredMediators(
187-
ticker: string,
191+
assetInput: string,
188192
params: RequiredMediatorsDto
189193
): ServiceReturn<void> {
190194
const {
191195
options,
192196
args: { mediators },
193197
} = extractTxOptions(params);
194-
const { addRequiredMediators } = await this.findOne(ticker);
198+
const { addRequiredMediators } = await this.findOne(assetInput);
195199

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

199203
public async removeRequiredMediators(
200-
ticker: string,
204+
assetInput: string,
201205
params: RequiredMediatorsDto
202206
): ServiceReturn<void> {
203207
const {
204208
options,
205209
args: { mediators },
206210
} = extractTxOptions(params);
207-
const { removeRequiredMediators } = await this.findOne(ticker);
211+
const { removeRequiredMediators } = await this.findOne(assetInput);
208212

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

212-
public async preApprove(ticker: string, params: TransactionBaseDto): ServiceReturn<void> {
216+
public async preApprove(assetInput: string, params: TransactionBaseDto): ServiceReturn<void> {
213217
const { options } = extractTxOptions(params);
214218

215219
const {
216220
settlements: { preApprove },
217-
} = await this.findOne(ticker);
221+
} = await this.findOne(assetInput);
218222

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

222-
public async removePreApproval(ticker: string, params: TransactionBaseDto): ServiceReturn<void> {
226+
public async removePreApproval(
227+
assetInput: string,
228+
params: TransactionBaseDto
229+
): ServiceReturn<void> {
223230
const { options } = extractTxOptions(params);
224231

225232
const {
226233
settlements: { removePreApproval },
227-
} = await this.findOne(ticker);
234+
} = await this.findOne(assetInput);
228235

229236
return this.transactionsService.submit(removePreApproval, {}, options);
230237
}
238+
239+
public async linkTickerToAsset(assetInput: string, params: LinkTickerDto): ServiceReturn<void> {
240+
const { options, args } = extractTxOptions(params);
241+
242+
const { linkTicker } = await this.findOne(assetInput);
243+
return this.transactionsService.submit(linkTicker, args, options);
244+
}
245+
246+
public async unlinkTickerFromAsset(
247+
assetInput: string,
248+
params: TransactionBaseDto
249+
): ServiceReturn<void> {
250+
const { options } = extractTxOptions(params);
251+
252+
const { unlinkTicker } = await this.findOne(assetInput);
253+
return this.transactionsService.submit(unlinkTicker, {}, options);
254+
}
231255
}

‎src/assets/dto/link-ticker.dto.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/* istanbul ignore file */
2+
import { ApiProperty } from '@nestjs/swagger';
3+
4+
import { IsTicker } from '~/common/decorators/validation';
5+
import { TransactionBaseDto } from '~/common/dto/transaction-base-dto';
6+
7+
export class LinkTickerDto extends TransactionBaseDto {
8+
@ApiProperty({
9+
description: 'Ticker to be linked with the Asset',
10+
example: 'TICKER',
11+
type: 'string',
12+
})
13+
@IsTicker()
14+
readonly ticker: string;
15+
}

0 commit comments

Comments
 (0)