@indiegems/gem-web-sdk 21.1.1 → 22.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/dist/function.d.ts +1 -0
- package/dist/game.d.ts +7 -1
- package/dist/gem-web-sdk.bridge.js +2 -2
- package/dist/gem-web-sdk.conformance.js +3 -3
- package/dist/gem-web-sdk.esm.js +3 -3
- package/dist/gem-web-sdk.function.js +2 -2
- package/dist/gem-web-sdk.game.js +3 -3
- package/dist/gem-web-sdk.min.js +3 -3
- package/dist/gem-web-sdk.sdk.js +3 -3
- package/dist/gem-web-sdk.server-api.js +1 -1
- package/dist/gem-web-sdk.server.js +2 -2
- package/dist/index.d.ts +15 -3
- package/dist/sdk.d.ts +8 -1
- package/dist/server-api.d.ts +1 -0
- package/dist/server.d.ts +3 -0
- package/docs/api/api-reference-function.md +1 -1
- package/docs/api/api-reference-game.md +152 -1
- package/docs/api/api-reference-server.md +28 -1
- package/docs/api/reference.json +114 -4
- package/docs/arcade-economy.md +3 -1
- package/docs/changelog.md +14 -0
- package/docs/daily-challenges.md +3 -1
- package/docs/getting-started.md +1 -1
- package/docs/inventory-and-catalogs.md +57 -2
- package/docs/manifest.json +16 -16
- package/docs/server-bundles.md +2 -2
- package/docs/serverless-functions.md +17 -0
- package/docs/what-a-game-must-do.md +22 -1
- package/package.json +1 -1
- package/schema/bridge.v1.json +68 -1
- package/schema/capabilities.public.json +6 -0
|
@@ -629,8 +629,32 @@ no fallback celebration.
|
|
|
629
629
|
`false` means the arcade will not show it here — the bridge is not up, or the embedder has no wallet
|
|
630
630
|
— and your game does not draw one of its own in its place.
|
|
631
631
|
|
|
632
|
-
Call it when your
|
|
633
|
-
|
|
632
|
+
Call it when your game decides to award, right beside the call that pays.
|
|
633
|
+
|
|
634
|
+
**When your serverless function pays it, give the cue and the call one `cueId`.** The platform's
|
|
635
|
+
event can reach the arcade before your `gem.functions.invoke` call even returns, so a cue sent after
|
|
636
|
+
it would otherwise look like a second reward. Make one id per award with `crypto.randomUUID()`, pass
|
|
637
|
+
it to `cueReward` and as the `cueId` of the `functions.invoke` that pays, and the arcade flies that
|
|
638
|
+
reward once, whichever reaches it first, so you may cue before the call or after it returns:
|
|
639
|
+
|
|
640
|
+
```js
|
|
641
|
+
const cueId = crypto.randomUUID(); // one per award, never reused
|
|
642
|
+
const result = await gem.functions.invoke({ claim: 'daily' }, { cueId });
|
|
643
|
+
gem.cueReward({ currencySlug: 'GEMS', expected: 15, origin, cueId });
|
|
644
|
+
```
|
|
645
|
+
|
|
646
|
+
A payout that pays Gems and Coins in one call cues each currency with the same `cueId`. The id is
|
|
647
|
+
yours and opaque: 1 to 64 letters, digits, `_` or `-`, and the SDK leaves out anything else with a
|
|
648
|
+
console warning rather than fail the cue or the call. It is for the animation only. It moves no
|
|
649
|
+
balance, your function never sees it, and no receipt or order carries it.
|
|
650
|
+
|
|
651
|
+
**A payout with no id** (your own game server's write, or a build that passes none) is paired by
|
|
652
|
+
currency and timing: cue it just before the write, or within about half a second after it.
|
|
653
|
+
|
|
654
|
+
**Either way, the arcade decides when the payout is shown.** A payout no cue has met within about
|
|
655
|
+
half a second flies from the middle of your frame, and a cue that comes after it adds nothing. If
|
|
656
|
+
your reward moment comes after your server pays, present the payouts yourself (below). Three things
|
|
657
|
+
to know either way:
|
|
634
658
|
|
|
635
659
|
- **The arcade never takes your word for the amount.** What lands, and the player's new total, come
|
|
636
660
|
from the platform's own event, sent only after the grant committed. `expected` is drawn as
|
|
@@ -648,6 +672,37 @@ not matter; the arcade reconciles. Three things to know:
|
|
|
648
672
|
- **This is presentation, not permission.** It grants nothing, spends nothing and reveals nothing,
|
|
649
673
|
and a reduced-motion player sees the total change without the flight.
|
|
650
674
|
|
|
675
|
+
#### Showing the payout at a moment of your own: `gem.setRewardPresentation('game')`
|
|
676
|
+
|
|
677
|
+
Your server pays when the reward is decided (the board is won, the run is verified), because that
|
|
678
|
+
is when the result is checked and the progress saved. Your reward moment can come seconds or minutes
|
|
679
|
+
later: the player taps **Claim** on the win card, or the coins fly on the screen they return to. Say
|
|
680
|
+
so once, at boot, and each payout waits for your cue:
|
|
681
|
+
|
|
682
|
+
```js
|
|
683
|
+
gem.setRewardPresentation('game'); // at boot
|
|
684
|
+
// ...your server pays when the board is won. Later, when the player taps Claim:
|
|
685
|
+
gem.cueReward({ currencySlug: 'ARCADE_TICKETS', expected: 20, origin: claimCoinCentre });
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
- **The payout supplies the number; your cue supplies the moment.** The arcade holds what each
|
|
689
|
+
payout paid for its currency instead of flying it from the middle of your frame. Each
|
|
690
|
+
`cueReward` releases up to its `expected` of what is held (all of it when you give no amount) and
|
|
691
|
+
flies it from its `origin`; the wallet chip over your game, when you show one
|
|
692
|
+
(`setWalletVisible`), counts up as the marks land.
|
|
693
|
+
- **Either order, any number.** A cue that finds nothing held waits for the next payout of its
|
|
694
|
+
currency, as it always has, and three payouts at the end of a level can be three cues, one cue,
|
|
695
|
+
or a cue per screen.
|
|
696
|
+
- **It delays the show, never the money.** What is paid, when, and every ceiling are unchanged; the
|
|
697
|
+
arcade's menu, its header and every purchase see the player's true total the moment the platform
|
|
698
|
+
pays. Only the chip over your game shows the total minus what is still held.
|
|
699
|
+
- **Nothing stays hidden.** Whatever part of a payout no cue releases within five minutes lands
|
|
700
|
+
quietly, with no flight. A player who quits on the win screen keeps every coin.
|
|
701
|
+
- **Screen state, held across a reconnect.** `gem.setRewardPresentation('arcade')` goes back to the
|
|
702
|
+
default and lands whatever is held. It returns `true` when the arcade was told, and `false` before
|
|
703
|
+
the bridge is up (held and sent on connect) and under an embedder that presents every payout its
|
|
704
|
+
own way; there, keep cueing as usual and nothing else changes.
|
|
705
|
+
|
|
651
706
|
To draw the Gem on your own screen, use the platform's art, which ships in the package — the Gems
|
|
652
707
|
**icon** beside an amount or price, the **hero** only on a reward presentation where the gem is the
|
|
653
708
|
subject. [brand-art.md](brand-art.md) has both, how to draw them, and the rest of the rules.
|
package/docs/manifest.json
CHANGED
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
},
|
|
29
29
|
{
|
|
30
30
|
"file": "arcade-economy.md",
|
|
31
|
-
"sha256": "
|
|
32
|
-
"updated": "2026-11-
|
|
31
|
+
"sha256": "108b3225a45dce776907344f8a3865b99b6e16598c6f70ddf0e8b923cf330a7e",
|
|
32
|
+
"updated": "2026-11-04T21:50:20Z"
|
|
33
33
|
},
|
|
34
34
|
{
|
|
35
35
|
"file": "async-opponents.md",
|
|
@@ -48,13 +48,13 @@
|
|
|
48
48
|
},
|
|
49
49
|
{
|
|
50
50
|
"file": "changelog.md",
|
|
51
|
-
"sha256": "
|
|
52
|
-
"updated": "2026-11-
|
|
51
|
+
"sha256": "8d83b78666b70888b6ecd2ffc6877956787daca3698eff5844e86f724699ce52",
|
|
52
|
+
"updated": "2026-11-04T23:21:33Z"
|
|
53
53
|
},
|
|
54
54
|
{
|
|
55
55
|
"file": "daily-challenges.md",
|
|
56
|
-
"sha256": "
|
|
57
|
-
"updated": "2026-
|
|
56
|
+
"sha256": "8bc76d80cf419c196fbdb052785c4a768408118389c311537df4229be6129366",
|
|
57
|
+
"updated": "2026-11-04T21:50:20Z"
|
|
58
58
|
},
|
|
59
59
|
{
|
|
60
60
|
"file": "dedicated-server-flow.md",
|
|
@@ -78,8 +78,8 @@
|
|
|
78
78
|
},
|
|
79
79
|
{
|
|
80
80
|
"file": "getting-started.md",
|
|
81
|
-
"sha256": "
|
|
82
|
-
"updated": "2026-11-
|
|
81
|
+
"sha256": "9cf851ec0894fd27350d492ce412ff025c86e17e2c9ecf900da7276868732540",
|
|
82
|
+
"updated": "2026-11-04T23:21:33Z"
|
|
83
83
|
},
|
|
84
84
|
{
|
|
85
85
|
"file": "graphics-route.md",
|
|
@@ -88,8 +88,8 @@
|
|
|
88
88
|
},
|
|
89
89
|
{
|
|
90
90
|
"file": "inventory-and-catalogs.md",
|
|
91
|
-
"sha256": "
|
|
92
|
-
"updated": "2026-11-
|
|
91
|
+
"sha256": "eea98a531d814113474ab381b1e404a0c0c30c8c770ebe5c68da853eab85564d",
|
|
92
|
+
"updated": "2026-11-04T23:07:19Z"
|
|
93
93
|
},
|
|
94
94
|
{
|
|
95
95
|
"file": "leaderboards.md",
|
|
@@ -143,8 +143,8 @@
|
|
|
143
143
|
},
|
|
144
144
|
{
|
|
145
145
|
"file": "server-bundles.md",
|
|
146
|
-
"sha256": "
|
|
147
|
-
"updated": "2026-11-
|
|
146
|
+
"sha256": "0f74e07ded6c9fb339c3bafa26e4af5153ce0bf20476fdb70444dcad2070766e",
|
|
147
|
+
"updated": "2026-11-04T23:21:33Z"
|
|
148
148
|
},
|
|
149
149
|
{
|
|
150
150
|
"file": "serverless-functions-js.md",
|
|
@@ -153,8 +153,8 @@
|
|
|
153
153
|
},
|
|
154
154
|
{
|
|
155
155
|
"file": "serverless-functions.md",
|
|
156
|
-
"sha256": "
|
|
157
|
-
"updated": "2026-
|
|
156
|
+
"sha256": "4c38d374fd80424be8ce774916e82253e68d8878d8b20558b815feec80ff4ee7",
|
|
157
|
+
"updated": "2026-11-04T21:50:20Z"
|
|
158
158
|
},
|
|
159
159
|
{
|
|
160
160
|
"file": "trading-and-gifting.md",
|
|
@@ -168,8 +168,8 @@
|
|
|
168
168
|
},
|
|
169
169
|
{
|
|
170
170
|
"file": "what-a-game-must-do.md",
|
|
171
|
-
"sha256": "
|
|
172
|
-
"updated": "2026-11-
|
|
171
|
+
"sha256": "e79c10bf3a06b7d9d85b5d6ba06a7a1cddf7a27f02d26b4bb4399ebf0d5a0644",
|
|
172
|
+
"updated": "2026-11-04T23:07:19Z"
|
|
173
173
|
}
|
|
174
174
|
]
|
|
175
175
|
}
|
package/docs/server-bundles.md
CHANGED
|
@@ -80,8 +80,8 @@ To check the download yourself: the package npm serves is, byte for byte, the fi
|
|
|
80
80
|
also publishes on its download host, with the hash beside it:
|
|
81
81
|
|
|
82
82
|
```bash
|
|
83
|
-
curl -fsSL -O https://downloads.gemarcade.com/sdk/
|
|
84
|
-
curl -fsSL https://downloads.gemarcade.com/sdk/
|
|
83
|
+
curl -fsSL -O https://downloads.gemarcade.com/sdk/v22.0.0/gem-web-sdk.tgz
|
|
84
|
+
curl -fsSL https://downloads.gemarcade.com/sdk/v22.0.0/gem-web-sdk.tgz.sri
|
|
85
85
|
openssl dgst -sha384 -binary gem-web-sdk.tgz | openssl base64 -A
|
|
86
86
|
```
|
|
87
87
|
|
|
@@ -716,6 +716,22 @@ try {
|
|
|
716
716
|
}
|
|
717
717
|
```
|
|
718
718
|
|
|
719
|
+
**A call that pays a reward the player sees takes a `cueId`.** Pass the same id to
|
|
720
|
+
`gem.cueReward` for that reward, one `crypto.randomUUID()` per award, and the Gem Arcade flies it
|
|
721
|
+
once whichever of the two reaches it first:
|
|
722
|
+
|
|
723
|
+
```js
|
|
724
|
+
const cueId = crypto.randomUUID();
|
|
725
|
+
const result = await gem.functions.invoke(claim, { cueId });
|
|
726
|
+
gem.cueReward({ currencySlug: 'GEMS', expected: 15, origin, cueId });
|
|
727
|
+
```
|
|
728
|
+
|
|
729
|
+
The platform carries it to the payout event it shows the arcade and nowhere else: your function
|
|
730
|
+
never sees it, and neither the answer nor a receipt carries it. It is 1 to 64 letters, digits, `_`
|
|
731
|
+
or `-`; the SDK leaves out anything else with a console warning and still makes the call.
|
|
732
|
+
[inventory-and-catalogs.md](inventory-and-catalogs.md#showing-the-reward-land-in-the-arcades-wallet)
|
|
733
|
+
has the cue itself.
|
|
734
|
+
|
|
719
735
|
⚠️ **A lost connection is `retryable` and tells you nothing about whether your function ran.** The
|
|
720
736
|
call left, and no answer came back. If your function grants or writes, make it safe to call twice
|
|
721
737
|
with the same input, for instance by keying what it writes on an id the caller sends.
|
|
@@ -882,6 +898,7 @@ the last column: it is `true` exactly where this table says to retry.
|
|
|
882
898
|
| `422 function_failed` | It did not finish. `err.failure` is `trap`, `memory_exceeded`, `cpu_budget_exceeded`, `host_call_limit`, `output_too_large` or `argument_too_large` | A bug or a limit in the function. Reproduce with the same input |
|
|
883
899
|
| `504 function_timeout` | It ran past the wall clock | Do not retry: the same input takes as long again |
|
|
884
900
|
| `404 function_not_published` | Nothing is promoted on the caller's channel | `gemctl function promote <version> --channel <channel>` |
|
|
901
|
+
| `400 invalid_cue_id` | The `cue_id` query parameter is not 1 to 64 letters, digits, `_` or `-`. `gem.functions.invoke` leaves a bad `cueId` out, so only a hand-written call meets it | Fix the hand-written call |
|
|
885
902
|
| `413 function_input_too_large` | The input is over 256 KiB | Send a request, not a file |
|
|
886
903
|
| `429 rate_limited` | This caller's per-minute budget is spent | Wait `err.retryAfterSeconds` |
|
|
887
904
|
| `503 function_capacity_unavailable` | The platform was too busy to run it. Your function is not known to be broken | Retry after `err.retryAfterSeconds` |
|
|
@@ -1785,7 +1785,9 @@ opens the Gem menu — the Gem button, in whichever corner it sits — to see th
|
|
|
1785
1785
|
achievement score, and every payout
|
|
1786
1786
|
[`gem.cueReward`](inventory-and-catalogs.md#showing-the-reward-land-in-the-arcades-wallet) announces
|
|
1787
1787
|
flies into the Gem button. A payout you do not cue flies too, from the middle of your frame, so cue
|
|
1788
|
-
the ones the player earns on screen and the flight starts where they are looking.
|
|
1788
|
+
the ones the player earns on screen and the flight starts where they are looking. A payout your
|
|
1789
|
+
serverless function makes takes the same `cueId` on the cue and on `gem.functions.invoke`, so it
|
|
1790
|
+
flies once.
|
|
1789
1791
|
|
|
1790
1792
|
**A game that wants them on screen asks** (the owner, 2026-10-29):
|
|
1791
1793
|
|
|
@@ -1804,6 +1806,25 @@ gem.setWalletVisible(true, { only: ['GEMS', 'ARCADE_TICKETS'] }); // add 'score'
|
|
|
1804
1806
|
- **An Arcade from before this change ignores the call**: nothing breaks, and the wallets stay in
|
|
1805
1807
|
the Gem menu. Another embedder that draws wallets over games honours it the same way.
|
|
1806
1808
|
|
|
1809
|
+
### When a payout is shown: `gem.setRewardPresentation`
|
|
1810
|
+
|
|
1811
|
+
By default the Arcade decides when a payout is shown: a cue within about half a second of your
|
|
1812
|
+
server's write flies it from the cue's `origin`, and a payout with no cue by then flies from the
|
|
1813
|
+
middle of your frame. **If your reward moment comes after your server pays** — a Claim button on
|
|
1814
|
+
the win card, or the screen the player returns to — say so once, at boot:
|
|
1815
|
+
|
|
1816
|
+
```js
|
|
1817
|
+
gem.setRewardPresentation('game');
|
|
1818
|
+
```
|
|
1819
|
+
|
|
1820
|
+
Each payout then waits for your `gem.cueReward`, which releases up to its `expected` and flies it
|
|
1821
|
+
from its `origin`, in either order and at any moment within five minutes; whatever no cue releases
|
|
1822
|
+
by then lands quietly, with no flight. The money never waits: what is paid and when is unchanged,
|
|
1823
|
+
and the Gem menu and every purchase see the player's true total at once. `'arcade'` goes back to
|
|
1824
|
+
the default.
|
|
1825
|
+
[inventory-and-catalogs.md](inventory-and-catalogs.md#showing-the-payout-at-a-moment-of-your-own-gemsetrewardpresentationgame)
|
|
1826
|
+
has the detail.
|
|
1827
|
+
|
|
1807
1828
|
## No accidental text selection
|
|
1808
1829
|
|
|
1809
1830
|
**You write nothing for this.** A long press on a phone, a double-click on a button, a drag across
|
package/package.json
CHANGED
package/schema/bridge.v1.json
CHANGED
|
@@ -2947,10 +2947,24 @@
|
|
|
2947
2947
|
"creator's ruling, 2026-09-24), so a game draws no Gems flight, counter or",
|
|
2948
2948
|
"celebration of its own. The same holds for Coins (`ARCADE_TICKETS`):",
|
|
2949
2949
|
"`currencySlug` decides what flies and which total moves. `origin` is the",
|
|
2950
|
-
"centre of the game's icon for that currency — its gem, or its coin."
|
|
2950
|
+
"centre of the game's icon for that currency — its gem, or its coin.",
|
|
2951
|
+
"",
|
|
2952
|
+
"`cueId` ties this cue to the grant it announces, so the arcade flies that",
|
|
2953
|
+
"reward ONCE whichever of the two reaches it first. The game chooses it —",
|
|
2954
|
+
"one `crypto.randomUUID()` per award — and passes the same one to the",
|
|
2955
|
+
"`gem.functions.invoke` that pays, which carries it to gem-api as the",
|
|
2956
|
+
"`cue_id` query parameter; gem-api echoes it as `cue_id` on every",
|
|
2957
|
+
"`platform_grant_fired` that invocation's grants send. Opaque and for",
|
|
2958
|
+
"animation only: it moves no balance, never names an order, and is never",
|
|
2959
|
+
"rendered. The SDK sends only 1 to 64 letters, digits, `_` or `-`; the",
|
|
2960
|
+
"wire bounds the length alone (no `format`, so a parent need not learn",
|
|
2961
|
+
"one), and a parent that compares it rechecks the characters itself and",
|
|
2962
|
+
"drops a malformed one, keeping the cue. Absent, the arcade pairs the cue",
|
|
2963
|
+
"and the grant by currency and timing, as before."
|
|
2951
2964
|
],
|
|
2952
2965
|
"fields": {
|
|
2953
2966
|
"currencySlug": { "kind": "string", "maxLength": 32 },
|
|
2967
|
+
"cueId": { "kind": "string", "maxLength": 64, "optional": true },
|
|
2954
2968
|
"expected": { "kind": "int", "min": 1, "max": 1000000000, "optional": true },
|
|
2955
2969
|
"origin": {
|
|
2956
2970
|
"kind": "object",
|
|
@@ -2964,6 +2978,59 @@
|
|
|
2964
2978
|
"rateLimit": { "perMinute": 60 }
|
|
2965
2979
|
},
|
|
2966
2980
|
|
|
2981
|
+
{
|
|
2982
|
+
"type": "gem.rewardPresentation",
|
|
2983
|
+
"direction": "frameToParent",
|
|
2984
|
+
"transport": "port",
|
|
2985
|
+
"$comment": [
|
|
2986
|
+
"Who decides WHEN a platform-currency payout from this game is shown landing:",
|
|
2987
|
+
"the arcade (`arcade`, what a parent does with no message at all) or the game",
|
|
2988
|
+
"(`game`). It changes the presentation only. What is paid, when the server pays",
|
|
2989
|
+
"it, the receipt, every ceiling and the player's ledger are the same either way.",
|
|
2990
|
+
"",
|
|
2991
|
+
"`arcade`: a payout the game cues within a beat of it flies from the cue's",
|
|
2992
|
+
"`origin`, and one it does not flies from the middle of the frame. A cue that",
|
|
2993
|
+
"arrives after its payout was already shown adds nothing.",
|
|
2994
|
+
"",
|
|
2995
|
+
"`game`: the game's cue supplies the moment and the payout supplies the number.",
|
|
2996
|
+
"A parent adds each payout to an amount it HOLDS for that currency instead of",
|
|
2997
|
+
"showing it, and its own wallet readout over the game shows the true total",
|
|
2998
|
+
"minus what it still holds, so a spend or a payout from anywhere else still",
|
|
2999
|
+
"moves it at once. A `gem.rewardCue` releases up to its `expected` of the held",
|
|
3000
|
+
"amount, or all of it when it names none, flying from its `origin`. A cue that",
|
|
3001
|
+
"finds nothing held waits for the next payout of its currency, as in `arcade`.",
|
|
3002
|
+
"So payouts and cues meet in either order and several at once need no pairing.",
|
|
3003
|
+
"Whatever part of a payout no cue has released within five minutes of its",
|
|
3004
|
+
"arrival lands quietly, with no flight: a game can delay its payout's",
|
|
3005
|
+
"presentation, never hide it.",
|
|
3006
|
+
"",
|
|
3007
|
+
"A cue still adds nothing to a wallet: what lands is always the payout's own",
|
|
3008
|
+
"figure. The parent's other surfaces (its menu, its header, every purchase)",
|
|
3009
|
+
"read the true total the moment the payout lands, so a held readout never stops",
|
|
3010
|
+
"a player spending what they have.",
|
|
3011
|
+
"",
|
|
3012
|
+
"SCREEN STATE, NOT A TOGGLE, like `gem.gemCorner`. Each message is the whole",
|
|
3013
|
+
"answer and the latest one wins. The SDK holds it from before the bridge is up",
|
|
3014
|
+
"and re-sends it after every handshake, so a parent that reconnects presents",
|
|
3015
|
+
"the way the game had asked. A switch back to `arcade` lands what is held.",
|
|
3016
|
+
"",
|
|
3017
|
+
"A PRESENTATION REQUEST ONLY, and not an authorization control. Cosmetic:",
|
|
3018
|
+
"nothing may key a trust decision on it.",
|
|
3019
|
+
"",
|
|
3020
|
+
"Gated by `rewardPresentation`: sent only to a parent that advertised it. To",
|
|
3021
|
+
"any other, `gem.setRewardPresentation` returns false, and that parent presents",
|
|
3022
|
+
"every payout the `arcade` way."
|
|
3023
|
+
],
|
|
3024
|
+
"fields": {
|
|
3025
|
+
"mode": {
|
|
3026
|
+
"kind": "enum",
|
|
3027
|
+
"values": ["arcade", "game"],
|
|
3028
|
+
"$comment": "`game` holds each payout's presentation for the game's cue; `arcade` is the default, for a parent that has had no message."
|
|
3029
|
+
}
|
|
3030
|
+
},
|
|
3031
|
+
"rateLimit": { "perMinute": 12 }
|
|
3032
|
+
},
|
|
3033
|
+
|
|
2967
3034
|
{
|
|
2968
3035
|
"type": "gem.walletDisplay",
|
|
2969
3036
|
"direction": "frameToParent",
|