Skip to content

Commit 3ebe04a

Browse files
authored
Merge pull request #462 from Concordium/validation-denyList-allowList-disabled
Disable validation on denyList/allowList at plt token transfers
2 parents 5796452 + e2cbc6a commit 3ebe04a

File tree

4 files changed

+164
-147
lines changed

4 files changed

+164
-147
lines changed

packages/sdk/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@
4747
with `buildAccountSigner`
4848
- A new optional field `createPlt` to `AuthorizationsV1` which exposes the access structure for PLT creation.
4949

50+
## 10.0.0-alpha.13
51+
52+
### Changed
53+
54+
- Disable `denyList`/`allowList` validation on plt token transfers.
55+
5056
## 10.0.0-alpha.12
5157

5258
### Breaking changes

packages/sdk/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@concordium/web-sdk",
3-
"version": "10.0.0-alpha.12",
3+
"version": "10.0.0-alpha.13",
44
"license": "Apache-2.0",
55
"engines": {
66
"node": ">=16"

packages/sdk/src/plt/Token.ts

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import {
1818
TokenAddDenyListOperation,
1919
TokenBurnOperation,
2020
TokenMintOperation,
21-
TokenModuleAccountState,
2221
TokenModuleState,
2322
TokenOperation,
2423
TokenOperationType,
@@ -375,26 +374,29 @@ export async function validateTransfer(
375374
throw new InsufficientFundsError(sender, TokenAmount.fromDecimal(payloadTotal, decimals));
376375
}
377376

378-
if (!token.moduleState.allowList && !token.moduleState.denyList) {
379-
// If the token neither has a deny list nor allow list, we can skip the check.
380-
return true;
381-
}
382-
383-
// Check that sender and all receivers are NOT on the deny list (if present), or that they are included in the allow list (if present).
384-
const receiverInfos = await Promise.all(payloads.map((p) => token.grpc.getAccountInfo(p.recipient.address)));
385-
const accounts = [senderInfo, ...receiverInfos];
386-
accounts.forEach((r) => {
387-
const accountToken = r.accountTokens.find((t) => t.id.value === token.info.id.value)?.state;
388-
const accountModuleState =
389-
accountToken?.moduleState === undefined
390-
? undefined
391-
: (Cbor.decode(accountToken.moduleState) as TokenModuleAccountState);
392-
393-
if (token.moduleState.denyList && accountModuleState?.denyList)
394-
throw new NotAllowedError(TokenHolder.fromAccountAddress(r.accountAddress));
395-
if (token.moduleState.allowList && !accountModuleState?.allowList)
396-
throw new NotAllowedError(TokenHolder.fromAccountAddress(r.accountAddress));
397-
});
377+
// FIXME: This is currently disable as a hacky-fix until some changes in the node are implemented/released.
378+
// https://linear.app/concordium/issue/COR-1650/empty-deny-list-blocking-transfer
379+
380+
// if (!token.moduleState.allowList && !token.moduleState.denyList) {
381+
// // If the token neither has a deny list nor allow list, we can skip the check.
382+
// return true;
383+
// }
384+
385+
// // Check that sender and all receivers are NOT on the deny list (if present), or that they are included in the allow list (if present).
386+
// const receiverInfos = await Promise.all(payloads.map((p) => token.grpc.getAccountInfo(p.recipient.address)));
387+
// const accounts = [senderInfo, ...receiverInfos];
388+
// accounts.forEach((r) => {
389+
// const accountToken = r.accountTokens.find((t) => t.id.value === token.info.id.value)?.state;
390+
// const accountModuleState =
391+
// accountToken?.moduleState === undefined
392+
// ? undefined
393+
// : (Cbor.decode(accountToken.moduleState) as TokenModuleAccountState);
394+
395+
// if (token.moduleState.denyList && accountModuleState?.denyList)
396+
// throw new NotAllowedError(TokenHolder.fromAccountAddress(r.accountAddress));
397+
// if (token.moduleState.allowList && !accountModuleState?.allowList)
398+
// throw new NotAllowedError(TokenHolder.fromAccountAddress(r.accountAddress));
399+
// });
398400

399401
return true;
400402
}

packages/sdk/test/ci/plt/Token.test.ts

Lines changed: 134 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -195,46 +195,49 @@ describe('Token.validateTransfer', () => {
195195
await expect(Token.validateTransfer(token, sender, transfer)).rejects.toThrow(Token.InsufficientFundsError);
196196
});
197197

198-
it('should throw NotAllowedError when account is on deny list', async () => {
199-
const sender = ACCOUNT_1;
200-
const recipient = ACCOUNT_2;
201-
const tokenId = TokenId.fromString('3f1bfce9');
202-
const decimals = 8;
203-
204-
// Setup token with deny list enabled
205-
const moduleState: TokenModuleState = {
206-
name: 'Test Token',
207-
metadata: TokenMetadataUrl.fromString('https://example.com/metadata'),
208-
paused: false,
209-
governanceAccount: TokenHolder.fromAccountAddress(sender),
210-
denyList: true,
211-
};
212-
213-
const token = createMockToken(decimals, moduleState, tokenId);
214-
215-
// Setup sender account with sufficient balance
216-
const senderBalance = TokenAmount.create(BigInt(1000), decimals);
217-
218-
// Create sender account info with deny list status
219-
const senderAccountState: TokenModuleAccountState = { denyList: true };
220-
const senderAccountInfo = createAccountInfo(sender, tokenId, senderBalance, senderAccountState);
221-
222-
// Mock getAccountInfo to return sender on deny list
223-
token.grpc.getAccountInfo = jest
224-
.fn()
225-
.mockResolvedValueOnce(senderAccountInfo) // First call for sender
226-
.mockResolvedValueOnce(createAccountInfo(recipient, tokenId)); // second call for recipient
227-
228-
// Create transfer payload
229-
const transferAmount = TokenAmount.create(BigInt(500), decimals);
230-
const transfer: TokenTransfer = {
231-
amount: transferAmount,
232-
recipient: TokenHolder.fromAccountAddress(recipient),
233-
};
234-
235-
// Should throw NotAllowedError
236-
await expect(Token.validateTransfer(token, sender, transfer)).rejects.toThrow(Token.NotAllowedError);
237-
});
198+
// FIXME: This is currently disable as a hacky-fix until some changes in the node are implemented/released.
199+
// https://linear.app/concordium/issue/COR-1650/empty-deny-list-blocking-transfer
200+
201+
// it('should throw NotAllowedError when account is on deny list', async () => {
202+
// const sender = ACCOUNT_1;
203+
// const recipient = ACCOUNT_2;
204+
// const tokenId = TokenId.fromString('3f1bfce9');
205+
// const decimals = 8;
206+
207+
// // Setup token with deny list enabled
208+
// const moduleState: TokenModuleState = {
209+
// name: 'Test Token',
210+
// metadata: TokenMetadataUrl.fromString('https://example.com/metadata'),
211+
// paused: false,
212+
// governanceAccount: TokenHolder.fromAccountAddress(sender),
213+
// denyList: true,
214+
// };
215+
216+
// const token = createMockToken(decimals, moduleState, tokenId);
217+
218+
// // Setup sender account with sufficient balance
219+
// const senderBalance = TokenAmount.create(BigInt(1000), decimals);
220+
221+
// // Create sender account info with deny list status
222+
// const senderAccountState: TokenModuleAccountState = { denyList: true };
223+
// const senderAccountInfo = createAccountInfo(sender, tokenId, senderBalance, senderAccountState);
224+
225+
// // Mock getAccountInfo to return sender on deny list
226+
// token.grpc.getAccountInfo = jest
227+
// .fn()
228+
// .mockResolvedValueOnce(senderAccountInfo) // First call for sender
229+
// .mockResolvedValueOnce(createAccountInfo(recipient, tokenId)); // second call for recipient
230+
231+
// // Create transfer payload
232+
// const transferAmount = TokenAmount.create(BigInt(500), decimals);
233+
// const transfer: TokenTransfer = {
234+
// amount: transferAmount,
235+
// recipient: TokenHolder.fromAccountAddress(recipient),
236+
// };
237+
238+
// // Should throw NotAllowedError
239+
// await expect(Token.validateTransfer(token, sender, transfer)).rejects.toThrow(Token.NotAllowedError);
240+
// });
238241

239242
it('should validate successfully for tokens with deny list when account does not have a balance', async () => {
240243
const sender = ACCOUNT_1;
@@ -276,91 +279,97 @@ describe('Token.validateTransfer', () => {
276279
await expect(Token.validateTransfer(token, sender, transfer)).resolves.toBe(true);
277280
});
278281

279-
it('should throw NotAllowedError for tokens with allow list when account does not have a balance', async () => {
280-
const sender = ACCOUNT_1;
281-
const recipient = ACCOUNT_2;
282-
const tokenId = TokenId.fromString('3f1bfce9');
283-
const decimals = 8;
284-
285-
// Setup token with deny list enabled
286-
const moduleState: TokenModuleState = {
287-
name: 'Test Token',
288-
metadata: TokenMetadataUrl.fromString('https://example.com/metadata'),
289-
governanceAccount: TokenHolder.fromAccountAddress(sender),
290-
allowList: true,
291-
paused: false,
292-
};
293-
294-
const token = createMockToken(decimals, moduleState, tokenId);
295-
296-
// Setup sender account with sufficient balance
297-
const senderBalance = TokenAmount.create(BigInt(1000), decimals);
298-
299-
// Create sender account info with deny list status
300-
const senderAccountState: TokenModuleAccountState = { allowList: true };
301-
const senderAccountInfo = createAccountInfo(sender, tokenId, senderBalance, senderAccountState);
302-
303-
// Mock getAccountInfo to return sender on deny list
304-
token.grpc.getAccountInfo = jest
305-
.fn()
306-
.mockResolvedValueOnce(senderAccountInfo) // First call for balance check
307-
.mockResolvedValueOnce(createAccountInfo(recipient)); // second call for recipient, no token balance.
308-
309-
// Create transfer payload
310-
const transferAmount = TokenAmount.create(BigInt(500), decimals);
311-
const transfer: TokenTransfer = {
312-
amount: transferAmount,
313-
recipient: TokenHolder.fromAccountAddress(recipient),
314-
};
315-
316-
// Should throw NotAllowedError
317-
await expect(Token.validateTransfer(token, sender, transfer)).rejects.toThrow(Token.NotAllowedError);
318-
});
319-
320-
it('should throw NotAllowedError when account is not on allow list', async () => {
321-
const sender = ACCOUNT_1;
322-
const recipient = ACCOUNT_2;
323-
const tokenId = TokenId.fromString('3f1bfce9');
324-
const decimals = 8;
325-
326-
// Setup token with allow list enabled
327-
const moduleState: TokenModuleState = {
328-
name: 'Test Token',
329-
metadata: TokenMetadataUrl.fromString('https://example.com/metadata'),
330-
paused: false,
331-
governanceAccount: TokenHolder.fromAccountAddress(sender),
332-
allowList: true,
333-
};
334-
335-
const token = createMockToken(decimals, moduleState, tokenId);
336-
337-
// Setup sender account with sufficient balance
338-
const senderBalance = TokenAmount.create(BigInt(1000), decimals);
339-
340-
// Create sender account info with allow list status set to true
341-
const senderAccountState: TokenModuleAccountState = { allowList: true };
342-
const senderAccountInfo = createAccountInfo(sender, tokenId, senderBalance, senderAccountState);
343-
344-
// Create recipient account info with allow list status set to true
345-
const recipientAccountState: TokenModuleAccountState = { allowList: false };
346-
const recipientAccountInfo = createAccountInfo(recipient, tokenId, undefined, recipientAccountState);
347-
348-
// Mock getAccountInfo to return sender not on allow list
349-
token.grpc.getAccountInfo = jest
350-
.fn()
351-
.mockResolvedValueOnce(senderAccountInfo) // First call for sender
352-
.mockResolvedValueOnce(recipientAccountInfo); // second call for recipient
353-
354-
// Create transfer payload
355-
const transferAmount = TokenAmount.create(BigInt(500), decimals);
356-
const transfer: TokenTransfer = {
357-
amount: transferAmount,
358-
recipient: TokenHolder.fromAccountAddress(recipient),
359-
};
360-
361-
// Should throw NotAllowedError
362-
await expect(Token.validateTransfer(token, sender, transfer)).rejects.toThrow(Token.NotAllowedError);
363-
});
282+
// FIXME: This is currently disable as a hacky-fix until some changes in the node are implemented/released.
283+
// https://linear.app/concordium/issue/COR-1650/empty-deny-list-blocking-transfer
284+
285+
// it('should throw NotAllowedError for tokens with allow list when account does not have a balance', async () => {
286+
// const sender = ACCOUNT_1;
287+
// const recipient = ACCOUNT_2;
288+
// const tokenId = TokenId.fromString('3f1bfce9');
289+
// const decimals = 8;
290+
291+
// // Setup token with deny list enabled
292+
// const moduleState: TokenModuleState = {
293+
// name: 'Test Token',
294+
// metadata: TokenMetadataUrl.fromString('https://example.com/metadata'),
295+
// governanceAccount: TokenHolder.fromAccountAddress(sender),
296+
// allowList: true,
297+
// paused: false,
298+
// };
299+
300+
// const token = createMockToken(decimals, moduleState, tokenId);
301+
302+
// // Setup sender account with sufficient balance
303+
// const senderBalance = TokenAmount.create(BigInt(1000), decimals);
304+
305+
// // Create sender account info with deny list status
306+
// const senderAccountState: TokenModuleAccountState = { allowList: true };
307+
// const senderAccountInfo = createAccountInfo(sender, tokenId, senderBalance, senderAccountState);
308+
309+
// // Mock getAccountInfo to return sender on deny list
310+
// token.grpc.getAccountInfo = jest
311+
// .fn()
312+
// .mockResolvedValueOnce(senderAccountInfo) // First call for balance check
313+
// .mockResolvedValueOnce(createAccountInfo(recipient)); // second call for recipient, no token balance.
314+
315+
// // Create transfer payload
316+
// const transferAmount = TokenAmount.create(BigInt(500), decimals);
317+
// const transfer: TokenTransfer = {
318+
// amount: transferAmount,
319+
// recipient: TokenHolder.fromAccountAddress(recipient),
320+
// };
321+
322+
// // Should throw NotAllowedError
323+
// await expect(Token.validateTransfer(token, sender, transfer)).rejects.toThrow(Token.NotAllowedError);
324+
// });
325+
326+
// FIXME: This is currently disable as a hacky-fix until some changes in the node are implemented/released.
327+
// https://linear.app/concordium/issue/COR-1650/empty-deny-list-blocking-transfer
328+
329+
// it('should throw NotAllowedError when account is not on allow list', async () => {
330+
// const sender = ACCOUNT_1;
331+
// const recipient = ACCOUNT_2;
332+
// const tokenId = TokenId.fromString('3f1bfce9');
333+
// const decimals = 8;
334+
335+
// // Setup token with allow list enabled
336+
// const moduleState: TokenModuleState = {
337+
// name: 'Test Token',
338+
// metadata: TokenMetadataUrl.fromString('https://example.com/metadata'),
339+
// paused: false,
340+
// governanceAccount: TokenHolder.fromAccountAddress(sender),
341+
// allowList: true,
342+
// };
343+
344+
// const token = createMockToken(decimals, moduleState, tokenId);
345+
346+
// // Setup sender account with sufficient balance
347+
// const senderBalance = TokenAmount.create(BigInt(1000), decimals);
348+
349+
// // Create sender account info with allow list status set to true
350+
// const senderAccountState: TokenModuleAccountState = { allowList: true };
351+
// const senderAccountInfo = createAccountInfo(sender, tokenId, senderBalance, senderAccountState);
352+
353+
// // Create recipient account info with allow list status set to true
354+
// const recipientAccountState: TokenModuleAccountState = { allowList: false };
355+
// const recipientAccountInfo = createAccountInfo(recipient, tokenId, undefined, recipientAccountState);
356+
357+
// // Mock getAccountInfo to return sender not on allow list
358+
// token.grpc.getAccountInfo = jest
359+
// .fn()
360+
// .mockResolvedValueOnce(senderAccountInfo) // First call for sender
361+
// .mockResolvedValueOnce(recipientAccountInfo); // second call for recipient
362+
363+
// // Create transfer payload
364+
// const transferAmount = TokenAmount.create(BigInt(500), decimals);
365+
// const transfer: TokenTransfer = {
366+
// amount: transferAmount,
367+
// recipient: TokenHolder.fromAccountAddress(recipient),
368+
// };
369+
370+
// // Should throw NotAllowedError
371+
// await expect(Token.validateTransfer(token, sender, transfer)).rejects.toThrow(Token.NotAllowedError);
372+
// });
364373

365374
it('should validate batch transfers when total amount is within balance', async () => {
366375
const sender = ACCOUNT_1;

0 commit comments

Comments
 (0)