You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
`dyn record` complements dynamic calls: while dynamic calls allow a program to route logic to any compliant callee, `dyn record` allows that same program to accept, inspect, and forward records from programs it has never seen at compile time, without losing the safety guarantees of the type system.
205
+
206
+
### Dynamic Records and Dynamic Calls
207
+
208
+
Regardless of what the interface signature says, dynamic calls always take dynamic records as inputs and return dynamic records as outputs.
209
+
210
+
When making a dynamic call, all record arguments are treated as `dyn record` under the hood, and all record return values come back as `dyn record` — even when the interface uses a concrete static record type. There are four cases depending on what the interface declares and what the caller provides:
211
+
212
+
**Case A — Interface expects `dyn record`, caller has `dyn record`**
213
+
214
+
Pass the dynamic record directly with no conversion needed.
215
+
216
+
```leo
217
+
interface ARC20 {
218
+
fn transfer_private(token: dyn record, to: address) -> dyn record;
219
+
}
220
+
221
+
program caller.aleo {
222
+
fn main(target: identifier, token: dyn record, to: address) -> dyn record {
223
+
return ARC20@(target)::transfer_private(token, to); // direct pass-through
224
+
}
225
+
}
226
+
```
227
+
228
+
**Case B — Interface expects `dyn record`, caller has a static record**
229
+
230
+
Convert the static record explicitly to `dyn record` using `as` before passing it.
231
+
232
+
```leo
233
+
interface ARC20 {
234
+
fn transfer_private(token: dyn record, to: address) -> dyn record;
235
+
}
236
+
237
+
program my_token.aleo : ARC20 {
238
+
record Token { owner: address, amount: u64 }
239
+
240
+
fn do_transfer(target: identifier, token: Token, to: address) -> dyn record {
241
+
return ARC20@(target)::transfer_private(token as dyn record, to); // explicit cast
242
+
}
243
+
}
244
+
```
245
+
246
+
**Case C — Interface expects a static record, caller has a static record**
247
+
248
+
Leo implicitly converts the static record to `dyn record` at the call site. Nothing extra is required from the caller, though an implicit unsafe step occurs under the hood.
249
+
250
+
```leo
251
+
interface ARC20 {
252
+
record Token;
253
+
fn transfer_private(token: Token, to: address) -> Token;
254
+
}
255
+
256
+
program caller.aleo {
257
+
record Token { owner: address, amount: u64 }
258
+
259
+
fn main(target: identifier, token: Token, to: address) -> dyn record {
260
+
return ARC20@(target)::transfer_private(token, to); // implicit conversion under the hood
261
+
}
262
+
}
263
+
```
264
+
265
+
**Case D — Interface expects a static record, caller has a `dyn record`**
266
+
267
+
Leo implicitly casts the dynamic record to the expected static type at the call site. The return value is still `dyn record`.
268
+
269
+
```leo
270
+
interface ARC20 {
271
+
record Token;
272
+
fn transfer_private(token: Token, to: address) -> Token;
273
+
}
274
+
275
+
program caller.aleo {
276
+
fn main(target: identifier, token: dyn record, to: address) -> dyn record {
277
+
return ARC20@(target)::transfer_private(token, to); // implicit cast, returns dyn record
278
+
}
279
+
}
280
+
```
281
+
282
+
In all four cases, the return type of a dynamic call that involves records is always `dyn record`, regardless of what the interface declares.
0 commit comments