-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathIou.cs
More file actions
417 lines (374 loc) · 20.6 KB
/
Copy pathIou.cs
File metadata and controls
417 lines (374 loc) · 20.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
// <auto-generated>
// This code was generated by daml-codegen-csharp.
// Do not edit this file manually.
// </auto-generated>
#nullable enable
using Daml.Ledger.Abstractions;
using Daml.Runtime.Commands;
using Daml.Runtime.Contracts;
using Daml.Runtime.Data;
using Daml.Runtime.Outcomes;
using Daml.Runtime.Stdlib;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace Quickstart;
/// <summary>
/// Generated from Daml template Iou:Iou
/// </summary>
public sealed partial record Iou([property: DamlFieldAttribute("issuer")] Party Issuer, [property: DamlFieldAttribute("owner")] Party Owner, [property: DamlFieldAttribute("currency")] string Currency, [property: DamlFieldAttribute("amount")] decimal Amount) : ITemplate
{
/// <summary>Gets the template identifier.</summary>
public static Identifier TemplateId { get; } = new("61d1c8472218a119a9e73167b9e9af82bfed91bf5ae5a89a82da27c10ea7f763", "Iou", "Iou");
/// <summary>Gets the package ID.</summary>
public static string PackageId => "61d1c8472218a119a9e73167b9e9af82bfed91bf5ae5a89a82da27c10ea7f763";
/// <summary>Gets the package name.</summary>
public static string PackageName => "quickstart";
/// <summary>Gets the package version.</summary>
public static Version PackageVersion { get; } = new(0, 0, 1);
/// <summary>Gets the compile-time Daml type descriptor.</summary>
public static DamlTypeDescriptor DamlTypeId { get; } = new(TemplateId, DamlTypeKind.Template, PackageName);
/// <summary>Converts this value to a DamlRecord.</summary>
public DamlRecord ToRecord() => DamlRecord.Create(
DamlField.Create("issuer", Issuer.ToDamlValue()),
DamlField.Create("owner", Owner.ToDamlValue()),
DamlField.Create("currency", new DamlText(Currency)),
DamlField.Create("amount", new DamlNumeric(Amount))
);
/// <summary>Creates an instance from a DamlRecord.</summary>
public static Iou FromRecord(DamlRecord record) => new Iou(
Issuer: Party.FromDamlValue(record.GetRequiredField("issuer").As<DamlParty>()),
Owner: Party.FromDamlValue(record.GetRequiredField("owner").As<DamlParty>()),
Currency: record.GetRequiredField("currency").As<DamlText>().Value,
Amount: record.GetRequiredField("amount").As<DamlNumeric>().Value
);
/// <summary>
/// Exercise the Archive choice.
/// This choice is consuming and will archive the contract.
/// </summary>
public static Choice<Iou, DamlUnit, DamlUnit> ChoiceArchive { get; } = new()
{
Name = new ChoiceName("Archive"),
Consuming = true,
ArgumentEncoder = _ => DamlRecord.Create(),
ResultDecoder = _ => DamlUnit.Instance
};
/// <summary>
/// Exercise the Transfer choice.
/// This choice is consuming and will archive the contract.
/// </summary>
public static Choice<Iou, Transfer, ContractId<Iou>> ChoiceTransfer { get; } = new()
{
Name = new ChoiceName("Transfer"),
Consuming = true,
ArgumentEncoder = arg => arg.ToRecord(),
ResultDecoder = val => new ContractId<Iou>(val.As<DamlContractId>().Value)
};
/// <summary>Contract ID for Iou.</summary>
public sealed record ContractId(string Value) : ContractId<Iou>(Value), IExercises<Iou>
{
ContractId<Iou> IExercises<Iou>.ContractId => this;
}
/// <summary>Active contract for Iou.</summary>
public sealed record Contract(ContractId Id, Iou Data) : IContract<ContractId, Iou>
{
/// <summary>Creates a Contract from a CreatedEvent.</summary>
public static Contract FromCreatedEvent(CreatedEvent @event) =>
new(new ContractId(@event.ContractId), Iou.FromRecord(@event.CreateArguments));
}
}
/// <summary>
/// Typed projection of the contracts created by the Transfer choice.
/// One field per template the choice creates; cardinality follows the choice's
/// return type (single, optional, list).
/// </summary>
public sealed record TransferResult(ContractId<Iou> Iou)
{
/// <summary>
/// Projects an upstream transaction's created contracts to a typed <see cref="TransferResult"/>.
/// Returns:
/// <list type="bullet">
/// <item><see cref="ExerciseOutcome{T}.One"/> when every expected template's cardinality matches.</item>
/// <item><see cref="ExerciseOutcome{T}.None"/> when at least one required slot's template is missing from the transaction.</item>
/// <item><see cref="ExerciseOutcome{T}.Many"/> when a single-cardinality slot has more than one created contract of its template, or an optional-cardinality slot has more than one.</item>
/// </list>
/// Cardinality is matched by template ID's <c>(module, entity)</c> pair only — package upgrades that share the same logical template name match cleanly.
/// </summary>
public static ExerciseOutcome<TransferResult> FromCreatedContracts(IEnumerable<CreatedContract> created)
{
ArgumentNullException.ThrowIfNull(created);
var templateMatches0 = new List<string>();
foreach (var item in created)
{
if (string.Equals(item.TemplateId.ModuleName, global::Quickstart.Iou.TemplateId.ModuleName, StringComparison.Ordinal)
&& string.Equals(item.TemplateId.EntityName, global::Quickstart.Iou.TemplateId.EntityName, StringComparison.Ordinal))
{
templateMatches0.Add(item.ContractId);
}
}
var matches0 = new List<string>();
var templateMatchIndex0 = 0;
if (templateMatchIndex0 < templateMatches0.Count)
{
matches0.Add(templateMatches0[templateMatchIndex0]);
templateMatchIndex0++;
}
while (templateMatchIndex0 < templateMatches0.Count)
{
matches0.Add(templateMatches0[templateMatchIndex0]);
templateMatchIndex0++;
}
if (matches0.Count == 0)
{
return new ExerciseOutcome<TransferResult>.None();
}
if (matches0.Count > 1)
{
return new ExerciseOutcome<TransferResult>.Many(matches0.Count, matches0);
}
return new ExerciseOutcome<TransferResult>.One(new TransferResult(
Iou: new ContractId<global::Quickstart.Iou>(matches0[0])
));
}
}
/// <summary>
/// Static <c><Choice>Async</c> extension methods for <see cref="Iou"/>.
/// One method per create-bearing choice; each delegates to
/// <see cref="global::Daml.Ledger.Abstractions.ILedgerWriter.TrySubmitAndWaitForTransactionAsync"/>
/// and projects success via <c><Choice>Result.FromCreatedContracts</c>.
/// </summary>
public static class IouExtensions
{
/// <summary>
/// Builds the <see cref="global::Daml.Runtime.Commands.ExerciseCommand"/> for the Transfer choice on this contract id.
/// </summary>
/// <param name="contractId">The contract on which to exercise the choice.</param>
/// <param name="argument">The choice argument.</param>
public static ExerciseCommand TransferCommand(
this ContractId<Iou> contractId,
Iou.Transfer argument)
{
ArgumentNullException.ThrowIfNull(contractId);
ArgumentNullException.ThrowIfNull(argument);
return new ExerciseCommand(
Iou.TemplateId,
contractId,
new ChoiceName("Transfer"),
argument.ToRecord());
}
/// <summary>
/// Exercises the Transfer choice and projects the resulting transaction's created contracts to a typed <see cref="TransferResult"/>.
/// One <c>Party</c> parameter is emitted per Daml controller (declaration order).
/// The wrapper builds a <see cref="SubmitterInfo"/> from those parties before
/// dispatching to <c>ILedgerWriter</c>.
/// </summary>
/// <param name="contractId">The contract on which to exercise the choice.</param>
/// <param name="client">The ledger client.</param>
/// <param name="argument">The choice argument.</param>
/// <param name="owner">Controller party from the Daml <c>controller</c> clause, routed into the submission's <c>actAs</c> set.</param>
/// <param name="workflowId">Optional workflow id; passed through to the ledger when supplied. No default — workflow IDs are correlation keys, and a per-choice default would bucket every submission of the same choice under one ID.</param>
/// <param name="commandId">Optional command id for deduplication; a fresh id is minted only when omitted. Pass the same id across a retry of a lost-but-accepted submission so the ledger deduplicates the resubmission instead of re-executing the choice.</param>
/// <param name="timeout">Optional per-call deadline, enforced server-side; the default <c>null</c> applies no deadline. An overrun surfaces as an <c>InfraError</c> outcome.</param>
/// <param name="cancellationToken">Cancellation token.</param>
public static async Task<ExerciseOutcome<TransferResult>> TransferAsync(
this ContractId<Iou> contractId,
ILedgerWriter client,
Iou.Transfer argument,
Party owner,
string? workflowId = null,
CommandId? commandId = null,
TimeSpan? timeout = null,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(client);
SubmitterInfo submitter = owner;
var command = contractId.TransferCommand(argument);
var submission = CommandsSubmission.Single(command)
.WithCommandId(commandId ?? new CommandId(Guid.NewGuid().ToString()));
if (!string.IsNullOrEmpty(workflowId))
{
submission = submission.WithWorkflowId(new WorkflowId(workflowId));
}
var outcome = await client.TrySubmitAndWaitForTransactionAsync(submission, submitter, timeout: timeout, cancellationToken: cancellationToken).ConfigureAwait(false);
return outcome.ProjectCommitted(tx => TransferResult.FromCreatedContracts(tx.CreatedContracts));
}
/// <summary>
/// Exercises the Transfer choice with an explicit <see cref="SubmitterInfo"/> and projects the resulting transaction's created contracts to a typed <see cref="TransferResult"/>.
/// Companion to the named-<c>Party</c> overload for the case where the submitter must
/// read contracts it does not act as — the choice's created contracts are visible to an
/// observer but not to the submitter, so the caller supplies the <c>readAs</c> parties.
/// </summary>
/// <param name="contractId">The contract on which to exercise the choice.</param>
/// <param name="client">The ledger client.</param>
/// <param name="argument">The choice argument.</param>
/// <param name="submitter">The submitter party set (<c>actAs</c> + optional <c>readAs</c>).</param>
/// <param name="workflowId">Optional workflow id; passed through to the ledger when supplied. No default — workflow IDs are correlation keys, and a per-choice default would bucket every submission of the same choice under one ID.</param>
/// <param name="commandId">Optional command id for deduplication; a fresh id is minted only when omitted. Pass the same id across a retry of a lost-but-accepted submission so the ledger deduplicates the resubmission instead of re-executing the choice.</param>
/// <param name="timeout">Optional per-call deadline, enforced server-side; the default <c>null</c> applies no deadline. An overrun surfaces as an <c>InfraError</c> outcome.</param>
/// <param name="cancellationToken">Cancellation token.</param>
public static async Task<ExerciseOutcome<TransferResult>> TransferAsync(
this ContractId<Iou> contractId,
ILedgerWriter client,
Iou.Transfer argument,
SubmitterInfo submitter,
string? workflowId = null,
CommandId? commandId = null,
TimeSpan? timeout = null,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(client);
var command = contractId.TransferCommand(argument);
var submission = CommandsSubmission.Single(command)
.WithCommandId(commandId ?? new CommandId(Guid.NewGuid().ToString()));
if (!string.IsNullOrEmpty(workflowId))
{
submission = submission.WithWorkflowId(new WorkflowId(workflowId));
}
var outcome = await client.TrySubmitAndWaitForTransactionAsync(submission, submitter, timeout: timeout, cancellationToken: cancellationToken).ConfigureAwait(false);
return outcome.ProjectCommitted(tx => TransferResult.FromCreatedContracts(tx.CreatedContracts));
}
/// <summary>
/// Exercises the Transfer choice on a fetched <see cref="Iou"/> contract,
/// reading every controller and observer party off the contract payload so the
/// caller passes no parties. Delegates to the
/// <c>ContractId<Iou></c> overload.
/// </summary>
/// <param name="contract">The fetched contract on which to exercise the choice.</param>
/// <param name="client">The ledger client.</param>
/// <param name="argument">The choice argument.</param>
/// <param name="workflowId">Optional workflow id; passed through to the ledger when supplied. No default — workflow IDs are correlation keys, and a per-choice default would bucket every submission of the same choice under one ID.</param>
/// <param name="commandId">Optional command id for deduplication; a fresh id is minted only when omitted. Pass the same id across a retry of a lost-but-accepted submission so the ledger deduplicates the resubmission instead of re-executing the choice.</param>
/// <param name="timeout">Optional per-call deadline, enforced server-side; the default <c>null</c> applies no deadline. An overrun surfaces as an <c>InfraError</c> outcome.</param>
/// <param name="cancellationToken">Cancellation token.</param>
public static Task<ExerciseOutcome<TransferResult>> TransferAsync(
this Iou.Contract contract,
ILedgerWriter client,
Iou.Transfer argument,
string? workflowId = null,
CommandId? commandId = null,
TimeSpan? timeout = null,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(contract);
ArgumentNullException.ThrowIfNull(client);
ArgumentNullException.ThrowIfNull(argument);
return contract.Id.TransferAsync(
client,
argument,
contract.Data.Owner,
workflowId,
commandId,
timeout,
cancellationToken);
}
}
/// <summary>
/// Typed-submitter extensions for <see cref="Iou"/>. The
/// <see cref="CreateAsync"/> overload derives the <c>actAs</c> parties
/// from the template payload (for signatories that are payload-field
/// references) and from the caller's explicit arguments (for signatories
/// the static analyzer could not resolve). When the template's
/// <c>observer</c> clause is statically resolvable, an
/// <c>Observers(payload)</c> documentation helper is also emitted.
/// </summary>
public static class IouSubmissionExtensions
{
/// <summary>
/// Creates a new <see cref="Iou"/> contract on the ledger.
/// The submitter is passed explicitly via <paramref name="submitter"/>. The static
/// analyzer could not resolve the Daml <c>signatory</c> clause to payload-field
/// references — typically because the expression involves the template key, a
/// constant, or a function call. <see cref="SubmitterInfo"/> implicitly converts
/// from a single <c>Party</c>, so single-party callers still pass one literal.
/// </summary>
/// <param name="client">The ledger client.</param>
/// <param name="payload">The contract payload.</param>
/// <param name="submitter">The submitter party set (<c>actAs</c> + optional <c>readAs</c>).</param>
/// <param name="cancellationToken">Cancellation token.</param>
public static Task<ExerciseOutcome<ContractId<Iou>>> CreateAsync(
this ILedgerWriter client,
Iou payload,
SubmitterInfo submitter,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(client);
ArgumentNullException.ThrowIfNull(payload);
return client.TryCreateAsync<Iou>(payload, submitter, cancellationToken: cancellationToken);
}
}
/// <summary>
/// Async exerciser extensions for <see cref="Iou"/> contract IDs whose choices
/// return a non-contract-id payload (Decimal, records, lists, Unit, etc.).
/// Each method submits the choice via
/// <c>ILedgerWriter.TrySubmitAndWaitForTransactionAsync</c> and lifts the typed result
/// into <c>ExerciseOutcome<TReturn></c>.
/// </summary>
public static class IouNonContractExtensions
{
/// <summary>
/// Builds the <see cref="global::Daml.Runtime.Commands.ExerciseCommand"/> for the Archive choice on this contract id.
/// </summary>
/// <param name="contractId">The contract on which to exercise the choice.</param>
public static ExerciseCommand ArchiveCommand(
this ContractId<Iou> contractId)
{
ArgumentNullException.ThrowIfNull(contractId);
return new ExerciseCommand(
Iou.TemplateId,
contractId,
new ChoiceName("Archive"),
DamlRecord.Create());
}
/// <summary>
/// Exercises the Archive choice and lifts the choice's exercise result to
/// <see cref="ExerciseOutcome{T}"/> over <c>Unit</c>. Structured Canton/Daml errors
/// and infrastructure/transport errors pass through unchanged.
/// </summary>
/// <param name="contractId">The contract on which to exercise the choice.</param>
/// <param name="client">The ledger client.</param>
/// <param name="actAs">The party submitting the command.</param>
/// <param name="workflowId">Optional workflow id; passed through to the ledger when supplied. No default — workflow IDs are correlation keys, and a per-choice default would bucket every submission of the same choice under one ID.</param>
/// <param name="commandId">Optional command id for deduplication; a fresh id is minted only when omitted. Pass the same id across a retry of a lost-but-accepted submission so the ledger deduplicates the resubmission instead of re-executing the choice.</param>
/// <param name="timeout">Optional per-call deadline, enforced server-side; the default <c>null</c> applies no deadline. An overrun surfaces as an <c>InfraError</c> outcome.</param>
/// <param name="cancellationToken">Cancellation token.</param>
public static async Task<ExerciseOutcome<Unit>> ArchiveAsync(
this ContractId<Iou> contractId,
ILedgerWriter client,
Party actAs,
string? workflowId = null,
CommandId? commandId = null,
TimeSpan? timeout = null,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(client);
var command = contractId.ArchiveCommand();
var submission = CommandsSubmission.Single(command)
.WithCommandId(commandId ?? new CommandId(Guid.NewGuid().ToString()));
if (!string.IsNullOrEmpty(workflowId))
{
submission = submission.WithWorkflowId(new WorkflowId(workflowId));
}
var outcome = await client.TrySubmitAndWaitForTransactionAsync(submission, actAs, timeout: timeout, cancellationToken: cancellationToken).ConfigureAwait(false);
return outcome.ProjectCommitted(tx => ProjectArchiveResult(tx, contractId.Value));
}
private static ExerciseOutcome<Unit> ProjectArchiveResult(TransactionResult tx, string contractId)
{
foreach (var exercised in tx.ExercisedEvents)
{
if (string.Equals(exercised.ContractId, contractId, StringComparison.Ordinal)
&& string.Equals(exercised.TemplateId.ModuleName, Iou.TemplateId.ModuleName, StringComparison.Ordinal)
&& string.Equals(exercised.TemplateId.EntityName, Iou.TemplateId.EntityName, StringComparison.Ordinal)
&& string.Equals(exercised.ChoiceName, "Archive", StringComparison.Ordinal))
{
return new ExerciseOutcome<Unit>.One(Unit.Value);
}
}
throw new InvalidOperationException(
$"Submission succeeded but no 'Archive' exercise on contract '{contractId}' was recorded on transaction {tx.UpdateId}. " +
"This is most often caused by the ILedgerWriter implementation not populating TransactionResult.ExercisedEvents — " +
"your ILedgerWriter implementation must project the transaction's exercised events into TransactionResult.ExercisedEvents. " +
"If your implementation does populate ExercisedEvents, ensure the participant is configured to return " +
"LedgerEffects with verbose events so the exercise event survives projection.");
}
}