Skip to content

Commit 74b0f55

Browse files
author
DA Automation
committed
[ci] Update Splice from CCI
Signed-off-by: DA Automation <splice-maintainers@digitalasset.com>
1 parent 9ea6760 commit 74b0f55

File tree

23 files changed

+720
-86
lines changed

23 files changed

+720
-86
lines changed

apps/common/src/main/scala/org/lfdecentralizedtrust/splice/environment/PackageVersionSupport.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,18 @@ trait PackageVersionSupport {
102102
)
103103
}
104104

105+
def supportsDescriptionInTransferPreapprovals(parties: Seq[PartyId], now: CantonTimestamp)(
106+
implicit tc: TraceContext
107+
): Future[FeatureSupport] = {
108+
isDarSupported(
109+
parties,
110+
PackageIdResolver.Package.SpliceWallet,
111+
now,
112+
// this is when the description field was added to transfer preapprovals
113+
DarResources.wallet_0_1_9,
114+
)
115+
}
116+
105117
private def isDarSupported(
106118
parties: Seq[PartyId],
107119
packageId: PackageIdResolver.Package,

apps/validator/src/main/scala/org/lfdecentralizedtrust/splice/validator/ValidatorApp.scala

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -746,6 +746,11 @@ class ValidatorApp(
746746
com.google.protobuf.duration.Duration
747747
.toJavaProto(DurationConversion.toProto(config.deduplicationDuration.asJavaApproximation))
748748
)
749+
synchronizerId <- scanConnection.getAmuletRulesDomain()(traceContext)
750+
packageVersionSupport = PackageVersionSupport.createPackageVersionSupport(
751+
synchronizerId,
752+
readOnlyLedgerConnection,
753+
)
749754
walletManagerOpt =
750755
if (config.enableWallet) {
751756
val externalPartyWalletManager = new ExternalPartyWalletManager(
@@ -777,6 +782,7 @@ class ValidatorApp(
777782
storage: Storage,
778783
retryProvider,
779784
scanConnection,
785+
packageVersionSupport,
780786
loggerFactory,
781787
domainMigrationInfo,
782788
participantId,
@@ -794,7 +800,6 @@ class ValidatorApp(
794800
logger.info("Not starting wallet as it's disabled")
795801
None
796802
}
797-
synchronizerId <- scanConnection.getAmuletRulesDomain()(traceContext)
798803
automation = new ValidatorAutomationService(
799804
config.automation,
800805
config.participantIdentitiesBackup,
@@ -830,10 +835,7 @@ class ValidatorApp(
830835
config.contactPoint,
831836
initialSynchronizerTime,
832837
loggerFactory,
833-
packageVersionSupport = PackageVersionSupport.createPackageVersionSupport(
834-
synchronizerId,
835-
readOnlyLedgerConnection,
836-
),
838+
packageVersionSupport,
837839
)
838840
_ <- config.appInstances.toList.traverse({ case (name, instance) =>
839841
appInitStep(s"Set up app instance $name") {
@@ -889,6 +891,11 @@ class ValidatorApp(
889891
loggerFactory,
890892
)
891893

894+
packageVersionSupport = PackageVersionSupport.createPackageVersionSupport(
895+
synchronizerId,
896+
readOnlyLedgerConnection,
897+
)
898+
892899
adminHandler =
893900
new HttpValidatorAdminHandler(
894901
automation,
@@ -899,17 +906,13 @@ class ValidatorApp(
899906
getAmuletRulesDomain = scanConnection.getAmuletRulesDomain,
900907
scanConnection = scanConnection,
901908
participantAdminConnection,
909+
packageVersionSupport,
902910
config,
903911
clock,
904912
retryProvider = retryProvider,
905913
loggerFactory,
906914
)
907915

908-
packageVersionSupport = PackageVersionSupport.createPackageVersionSupport(
909-
synchronizerId,
910-
readOnlyLedgerConnection,
911-
)
912-
913916
walletInternalHandler = walletManagerOpt.map(walletManager =>
914917
new HttpWalletHandler(
915918
walletManager,

apps/validator/src/main/scala/org/lfdecentralizedtrust/splice/validator/admin/http/HttpValidatorAdminHandler.scala

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import org.lfdecentralizedtrust.splice.codegen.java.splice.wallet.install.amulet
1919
import org.lfdecentralizedtrust.splice.environment.ledger.api.DedupOffset
2020
import org.lfdecentralizedtrust.splice.environment.{
2121
BaseLedgerConnection,
22+
PackageVersionSupport,
2223
ParticipantAdminConnection,
2324
RetryFor,
2425
RetryProvider,
@@ -73,6 +74,7 @@ class HttpValidatorAdminHandler(
7374
getAmuletRulesDomain: GetAmuletRulesDomain,
7475
scanConnection: ScanConnection,
7576
participantAdminConnection: ParticipantAdminConnection,
77+
packageVersionSupport: PackageVersionSupport,
7678
config: ValidatorAppBackendConfig,
7779
clock: Clock,
7880
retryProvider: RetryProvider,
@@ -761,6 +763,12 @@ class HttpValidatorAdminHandler(
761763
}
762764
}
763765
externalPartyAmuletRules <- scanConnection.getExternalPartyAmuletRules()
766+
supportsDescription <- packageVersionSupport
767+
.supportsDescriptionInTransferPreapprovals(
768+
Seq(receiverParty, senderParty, store.key.dsoParty),
769+
clock.now,
770+
)
771+
.map(_.supported)
764772
commands = externalPartyAmuletRules.toAssignedContract
765773
.getOrElse(
766774
throw Status.Code.FAILED_PRECONDITION.toStatus
@@ -777,7 +785,7 @@ class HttpValidatorAdminHandler(
777785
body.amount.bigDecimal,
778786
body.expiresAt.toInstant,
779787
body.nonce,
780-
body.description.toJava,
788+
Option.when(supportsDescription)(body.description).flatten.toJava,
781789
)
782790
)
783791
.update

apps/wallet/frontend/src/__tests__/wallet.test.tsx

Lines changed: 60 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,17 @@ const dsoEntry = nameServiceEntries.find(e => e.name.startsWith('dso'))!;
2323

2424
const walletUrl = window.splice_config.services.validator.url;
2525

26-
function featureSupportHandler(tokenStandardSupported: boolean) {
26+
function featureSupportHandler(
27+
tokenStandardSupported: boolean,
28+
transferPreapprovalDescriptionSupported: boolean
29+
) {
2730
return rest.get(`${walletUrl}/v0/feature-support`, async (_, res, ctx) => {
28-
return res(ctx.json({ token_standard: tokenStandardSupported }));
31+
return res(
32+
ctx.json({
33+
token_standard: tokenStandardSupported,
34+
transfer_preapproval_description: transferPreapprovalDescriptionSupported,
35+
})
36+
);
2937
});
3038
}
3139

@@ -100,7 +108,7 @@ describe('Wallet user can', () => {
100108
});
101109

102110
test('not see dso in list of transfer-offer receivers', async () => {
103-
server.use(featureSupportHandler(true));
111+
server.use(featureSupportHandler(true, true));
104112
const user = userEvent.setup();
105113
render(
106114
<WalletConfigProvider>
@@ -133,7 +141,7 @@ describe('Wallet user can', () => {
133141
transferTests(false);
134142

135143
test('fall back to non-token standard transfers when the token standard is not supported', async () => {
136-
server.use(featureSupportHandler(false));
144+
server.use(featureSupportHandler(false, true));
137145

138146
const user = userEvent.setup();
139147
render(
@@ -192,6 +200,43 @@ describe('Wallet user can', () => {
192200
// The withdraw has a dummy conversion rate of 0 so no amulet conversion rate is displayed
193201
expect(await screen.findAllByText('@')).toHaveLength(3);
194202
});
203+
204+
test('transfer preapproval (without token standard) does not show nor send description if not supported', async () => {
205+
// token standard as not supported
206+
server.use(featureSupportHandler(false, false));
207+
const user = userEvent.setup();
208+
render(
209+
<WalletConfigProvider>
210+
<App />
211+
</WalletConfigProvider>
212+
);
213+
expect(await screen.findByText('Transfer')).toBeDefined();
214+
215+
const transferOffersLink = screen.getByRole('link', { name: 'Transfer' });
216+
await user.click(transferOffersLink);
217+
expect(screen.getByRole('heading', { name: 'Transfers' })).toBeDefined();
218+
219+
const receiverInput = screen
220+
.getAllByRole('combobox')
221+
.find(e => e.id === 'create-offer-receiver')!;
222+
fireEvent.change(receiverInput, { target: { value: 'bob::preapproval' } });
223+
await vi.waitFor(() => expect(screen.getByRole('button', { name: 'Send' })).toBeEnabled());
224+
225+
// there should be no description input
226+
expect(screen.queryByRole('textbox', { name: 'description' })).not.toBeInTheDocument();
227+
228+
await user.click(screen.getByRole('button', { name: 'Send' }));
229+
230+
await assertCorrectMockIsCalled(
231+
true,
232+
{
233+
amount: '1.0',
234+
receiver_party_id: 'bob::preapproval',
235+
// description omitted: it is not sent
236+
},
237+
true
238+
);
239+
});
195240
}, 7500);
196241

197242
function transferTests(disableTokenStandard: boolean) {
@@ -203,7 +248,7 @@ function transferTests(disableTokenStandard: boolean) {
203248
}
204249

205250
test('transfer offer is used when receiver has no transfer preapproval', async () => {
206-
server.use(featureSupportHandler(true));
251+
server.use(featureSupportHandler(true, true));
207252
const user = userEvent.setup();
208253
render(
209254
<WalletConfigProvider>
@@ -236,7 +281,7 @@ function transferTests(disableTokenStandard: boolean) {
236281
});
237282

238283
test('transfer preapproval is used when receiver has a transfer preapproval', async () => {
239-
server.use(featureSupportHandler(true));
284+
server.use(featureSupportHandler(true, true));
240285
const user = userEvent.setup();
241286
render(
242287
<WalletConfigProvider>
@@ -270,7 +315,7 @@ function transferTests(disableTokenStandard: boolean) {
270315
});
271316

272317
test('transfer offer is used when receiver has a transfer preapproval but checkbox is unchecked', async () => {
273-
server.use(featureSupportHandler(true));
318+
server.use(featureSupportHandler(true, true));
274319
const user = userEvent.setup();
275320
render(
276321
<WalletConfigProvider>
@@ -304,7 +349,7 @@ function transferTests(disableTokenStandard: boolean) {
304349
});
305350

306351
test('deduplication id is passed', async () => {
307-
server.use(featureSupportHandler(true));
352+
server.use(featureSupportHandler(true, true));
308353
const user = userEvent.setup();
309354
render(
310355
<WalletConfigProvider>
@@ -370,7 +415,7 @@ function transferTests(disableTokenStandard: boolean) {
370415

371416
async function assertCorrectMockIsCalled(
372417
usesRegularTransferOffer: boolean,
373-
expected: { amount: string; receiver_party_id: string; description: string },
418+
expected: { amount: string; receiver_party_id: string; description?: string },
374419
isPreapproval: boolean
375420
) {
376421
if (!usesRegularTransferOffer) {
@@ -383,6 +428,12 @@ async function assertCorrectMockIsCalled(
383428
expect(requestMocks.transferPreapprovalSend).toHaveBeenCalledWith(
384429
expect.objectContaining(expected)
385430
);
431+
// unfortunately 'objectContaining' does not work for `description: undefined`:
432+
// the mock omits the field and the expected has it as undefined.
433+
// Omitting `description` from `expected` doesn't work because then it's not checked at all.
434+
expect(requestMocks.transferPreapprovalSend.mock.lastCall![0].description).equals(
435+
expected.description
436+
);
386437
expect(requestMocks.createTransferOffer).not.toHaveBeenCalled();
387438
expect(requestMocks.createTransferViaTokenStandard).not.toHaveBeenCalled();
388439
} else {

apps/wallet/frontend/src/components/SendTransfer.tsx

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -252,18 +252,20 @@ const SendTransfer: React.FC = () => {
252252
</FormControl>
253253
</Stack>
254254
)}
255-
<Stack direction="column" mb={4} spacing={1}>
256-
<Typography variant="h6">
257-
Description <Typography variant="caption">(optional)</Typography>{' '}
258-
</Typography>
259-
<TextField
260-
id="create-offer-description"
261-
rows={4}
262-
multiline
263-
inputProps={{ 'aria-label': 'description' }}
264-
onChange={e => setDescription(e.target.value)}
265-
/>
266-
</Stack>
255+
{featureSupport.data?.transferPreapprovalDescription ? (
256+
<Stack direction="column" mb={4} spacing={1}>
257+
<Typography variant="h6">
258+
Description <Typography variant="caption">(optional)</Typography>{' '}
259+
</Typography>
260+
<TextField
261+
id="create-offer-description"
262+
rows={4}
263+
multiline
264+
inputProps={{ 'aria-label': 'description' }}
265+
onChange={e => setDescription(e.target.value)}
266+
/>
267+
</Stack>
268+
) : null}
267269

268270
<DisableConditionally
269271
conditions={[

apps/wallet/frontend/src/contexts/WalletServiceContext.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ export const WalletClientProvider: React.FC<React.PropsWithChildren<WalletProps>
276276
receiver_party_id: receiverPartyId,
277277
amount: amount.isInteger() ? amount.toFixed(1) : amount.toString(),
278278
deduplication_id: deduplicationId,
279-
description,
279+
description: description === '' ? undefined : description,
280280
};
281281
await walletClient.transferPreapprovalSend(request);
282282
},

apps/wallet/frontend/src/hooks/useFeatureSupport.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,19 @@
33
import { useQuery, UseQueryResult } from '@tanstack/react-query';
44
import { useWalletClient } from '../contexts/WalletServiceContext';
55

6-
export const useFeatureSupport = (): UseQueryResult<{
6+
interface WalletFeatureSupport {
77
tokenStandard: boolean;
8-
}> => {
8+
transferPreapprovalDescription: boolean;
9+
}
10+
export const useFeatureSupport = (): UseQueryResult<WalletFeatureSupport> => {
911
const walletClient = useWalletClient();
1012
return useQuery({
1113
queryKey: ['featureSupport'],
1214
queryFn: async () => {
1315
const result = await walletClient.featureSupport();
1416
return {
1517
tokenStandard: result.token_standard,
18+
transferPreapprovalDescription: result.transfer_preapproval_description,
1619
};
1720
},
1821
});

apps/wallet/src/main/openapi/wallet-internal.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1275,6 +1275,9 @@ components:
12751275
type: object
12761276
required:
12771277
- token_standard
1278+
- transfer_preapproval_description
12781279
properties:
12791280
token_standard:
12801281
type: boolean
1282+
transfer_preapproval_description:
1283+
type: boolean

apps/wallet/src/main/scala/org/lfdecentralizedtrust/splice/wallet/UserWalletManager.scala

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@ import org.apache.pekko.stream.Materializer
77
import org.lfdecentralizedtrust.splice.codegen.java.splice.amulet as amuletCodegen
88
import org.lfdecentralizedtrust.splice.codegen.java.splice.wallet.install.WalletAppInstall
99
import org.lfdecentralizedtrust.splice.config.AutomationConfig
10-
import org.lfdecentralizedtrust.splice.environment.{SpliceLedgerClient, RetryProvider}
10+
import org.lfdecentralizedtrust.splice.environment.{
11+
PackageVersionSupport,
12+
RetryProvider,
13+
SpliceLedgerClient,
14+
}
1115
import org.lfdecentralizedtrust.splice.environment.ledger.api.DedupDuration
1216
import org.lfdecentralizedtrust.splice.migration.DomainMigrationInfo
1317
import org.lfdecentralizedtrust.splice.scan.admin.api.client.BftScanConnection
@@ -52,6 +56,7 @@ class UserWalletManager(
5256
storage: Storage,
5357
retryProvider: RetryProvider,
5458
scanConnection: BftScanConnection,
59+
packageVersionSupport: PackageVersionSupport,
5560
override val loggerFactory: NamedLoggerFactory,
5661
domainMigrationInfo: DomainMigrationInfo,
5762
participantId: ParticipantId,
@@ -227,6 +232,7 @@ class UserWalletManager(
227232
userRetryProvider,
228233
userLoggerFactory,
229234
scanConnection,
235+
packageVersionSupport,
230236
domainMigrationInfo,
231237
participantId,
232238
ingestFromParticipantBegin,

apps/wallet/src/main/scala/org/lfdecentralizedtrust/splice/wallet/UserWalletService.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ class UserWalletService(
4646
override protected[this] val retryProvider: RetryProvider,
4747
override val loggerFactory: NamedLoggerFactory,
4848
scanConnection: BftScanConnection,
49+
packageVersionSupport: PackageVersionSupport,
4950
domainMigrationInfo: DomainMigrationInfo,
5051
participantId: ParticipantId,
5152
ingestFromParticipantBegin: Boolean,
@@ -102,6 +103,7 @@ class UserWalletService(
102103
domainUnpausedSync,
103104
scanConnection,
104105
retryProvider,
106+
packageVersionSupport,
105107
ingestFromParticipantBegin,
106108
ingestUpdateHistoryFromParticipantBegin,
107109
loggerFactory,

0 commit comments

Comments
 (0)