Skip to content

Commit 1cc9472

Browse files
feat(frontend): Service to call the transfer method of EXT token canister (#10627)
# Motivation Like the other methods, we create an API service to call method `transfer` of `ExtV2TokenCanister`.
1 parent e553b2e commit 1cc9472

File tree

2 files changed

+79
-6
lines changed

2 files changed

+79
-6
lines changed

src/frontend/src/icp/api/ext-v2-token.api.ts

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export const transactions = async ({
4747
* @param {boolean} [params.certified=true] - Whether the data should be certified.
4848
* @param {OptionIdentity} params.identity - The identity to use for the request.
4949
* @param {CanisterIdText} params.canisterId - The canister ID of the EXT v2 token.
50-
* @param {TokenIdentifier} params.tokenIdentifier - The token identifier as string.
50+
* @param {TokenIdentifier} params.tokenIdentifier - The token identifier of the NFT as string.
5151
* @param {QueryParams} params.rest - Additional query parameters.
5252
* @returns {Promise<Balance>} The user's balance for the specified token.
5353
*/
@@ -82,17 +82,17 @@ export const balance = async ({
8282
* @link https://github.com/Toniq-Labs/ext-v2-token/blob/main/API-REFERENCE.md#tokens_ext
8383
*
8484
* @param {Object} params - The parameters for fetching the tokens.
85-
* @param {Principal} params.owner - The principal of the owner whose tokens should be fetched.
85+
* @param {boolean} [params.certified=true] - Whether the data should be certified.
8686
* @param {OptionIdentity} params.identity - The identity to use for the request.
8787
* @param {CanisterIdText} params.canisterId - The canister ID of the EXT v2 token.
88-
* @param {boolean} [params.certified=true] - Whether the data should be certified.
88+
* @param {Principal} params.owner - The principal of the owner whose tokens should be fetched.
8989
* @returns {Promise<TokenIndex[]>} The list of token indices owned by the user.
9090
* @throws CanisterInternalError if the token identifier is invalid.
9191
*/
9292
export const getTokensByOwner = async ({
93+
certified,
9394
identity,
9495
owner,
95-
certified,
9696
canisterId,
9797
...rest
9898
}: CanisterApiFunctionParamsWithCanisterId<{ owner: Principal } & QueryParams>): Promise<
@@ -111,6 +111,41 @@ export const getTokensByOwner = async ({
111111
return await getTokensByOwner({ certified, ...getIcrcAccount(owner) });
112112
};
113113

114+
/**
115+
* Transfer NFT of a collection from one user to another.
116+
*
117+
* @link https://github.com/Toniq-Labs/ext-v2-token/blob/main/API-REFERENCE.md#transfer--ext_transfer
118+
*
119+
* @param {Object} params - The parameters for the transfer.
120+
* @param {boolean} [params.certified=true] - Whether the data should be certified.
121+
* @param {OptionIdentity} params.identity - The identity to use for the request.
122+
* @param {CanisterIdText} params.canisterId - The canister ID of the EXT v2 token.
123+
* @param {Principal} params.from - The ICRC principal of the sender.
124+
* @param {Principal} params.to - The ICRC principal of the receiver.
125+
* @param {TokenIdentifier} params.tokenIdentifier - The token identifier of the NFT as string.
126+
* @param {bigint} params.amount - The amount to transfer.
127+
*/
128+
export const transfer = async ({
129+
certified,
130+
identity,
131+
canisterId,
132+
from,
133+
to,
134+
tokenIdentifier,
135+
amount,
136+
...rest
137+
}: CanisterApiFunctionParamsWithCanisterId<
138+
{ from: Principal; to: Principal; tokenIdentifier: TokenIdentifier; amount: bigint } & QueryParams
139+
>) => {
140+
const { transfer } = await extV2TokenCanister({
141+
identity,
142+
canisterId,
143+
...rest
144+
});
145+
146+
await transfer({ certified, from, to, tokenIdentifier, amount });
147+
};
148+
114149
const extV2TokenCanister = async ({
115150
identity,
116151
nullishIdentityErrorMessage,

src/frontend/src/tests/icp/api/ext-v2-token.api.spec.ts

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import { balance, getTokensByOwner, transactions } from '$icp/api/ext-v2-token.api';
1+
import { balance, getTokensByOwner, transactions, transfer } from '$icp/api/ext-v2-token.api';
22
import { ExtV2TokenCanister } from '$icp/canisters/ext-v2-token.canister';
33
import { ZERO } from '$lib/constants/app.constants';
44
import {
55
mockExtV2TokenCanisterId,
66
mockExtV2TokenIdentifier,
77
mockExtV2Transactions
88
} from '$tests/mocks/ext-v2-token.mock';
9-
import { mockIdentity, mockPrincipal } from '$tests/mocks/identity.mock';
9+
import { mockIdentity, mockPrincipal, mockPrincipal2 } from '$tests/mocks/identity.mock';
1010
import { mock } from 'vitest-mock-extended';
1111

1212
describe('ext-v2-token.api', () => {
@@ -111,4 +111,42 @@ describe('ext-v2-token.api', () => {
111111
expect(tokenCanisterMock.getTokensByOwner).not.toHaveBeenCalled();
112112
});
113113
});
114+
115+
describe('transfer', () => {
116+
const mockBalance = 456n;
117+
118+
const params = {
119+
identity: mockIdentity,
120+
canisterId: mockExtV2TokenCanisterId,
121+
from: mockPrincipal,
122+
to: mockPrincipal2,
123+
tokenIdentifier: mockExtV2TokenIdentifier,
124+
amount: 123n
125+
};
126+
127+
const expectedParams = {
128+
from: mockPrincipal,
129+
to: mockPrincipal2,
130+
tokenIdentifier: mockExtV2TokenIdentifier,
131+
amount: 123n
132+
};
133+
134+
beforeEach(() => {
135+
tokenCanisterMock.transfer.mockResolvedValue(mockBalance);
136+
});
137+
138+
it('should call successfully transfer endpoint', async () => {
139+
await transfer(params);
140+
141+
expect(tokenCanisterMock.transfer).toHaveBeenCalledExactlyOnceWith(expectedParams);
142+
});
143+
144+
it('should raise an error if identity is nullish', async () => {
145+
await expect(transfer({ ...params, identity: undefined })).rejects.toThrow();
146+
147+
await expect(transfer({ ...params, identity: null })).rejects.toThrow();
148+
149+
expect(tokenCanisterMock.transfer).not.toHaveBeenCalled();
150+
});
151+
});
114152
});

0 commit comments

Comments
 (0)