@xgamesfun/solana 0.24.0 → 0.25.1
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/DISCLAIMER.md +1 -1
- package/NOTICE +4 -4
- package/README.md +100 -52
- package/bin/xsolana.mjs +32 -11
- package/examples/blind-forge.json +2 -4
- package/examples/coin-flip-arena.json +2 -5
- package/examples/duel-arena.json +1 -4
- package/examples/fair-presale.json +1 -3
- package/examples/hero-trials.json +14 -2
- package/examples/ideas.json +788 -788
- package/examples/nft-raffle.json +1 -0
- package/examples/partner-vault.json +19 -3
- package/examples/sacrifice-raffle.json +2 -10
- package/examples/shelter-fair.json +1 -0
- package/examples/stakers-draw.json +4 -7
- package/examples/trade-in-shop.json +2 -3
- package/js/approve/details.mjs +117 -48
- package/js/approve/ed25519.mjs +5 -1
- package/js/approve/live-check.mjs +83 -26
- package/js/approve/page.html +217 -78
- package/js/approve/page.js +43 -15
- package/js/approve/server.mjs +276 -74
- package/js/chain/base58.js +7 -1
- package/js/chain/borsh.js +15 -11
- package/js/chain/bytes.js +11 -8
- package/js/chain/core.js +21 -5
- package/js/chain/curve.js +1 -1
- package/js/chain/lookup.js +118 -36
- package/js/chain/mint.js +6 -2
- package/js/chain/pda.js +2 -2
- package/js/chain/programs.js +6 -6
- package/js/chain/rpc.js +40 -14
- package/js/chain/token.js +13 -2
- package/js/chain/token2022.js +55 -16
- package/js/chain/tx.js +32 -22
- package/js/client/accounts.js +270 -80
- package/js/client/compressed.js +4 -3
- package/js/client/grant.js +165 -40
- package/js/client/idl/loot.js +1 -1
- package/js/client/idl/loot.json +104 -16
- package/js/client/ix.js +2121 -457
- package/js/client/pda.js +20 -18
- package/js/client/policy.js +12 -5
- package/js/client/random.js +2 -2
- package/js/client/sizes.js +220 -65
- package/js/client/traits.js +14 -5
- package/js/connect/connect.mjs +59 -17
- package/js/economy/check.mjs +3884 -690
- package/js/economy/cost.mjs +381 -57
- package/js/economy/examples.mjs +176 -35
- package/js/economy/explain.mjs +468 -110
- package/js/economy/guide.mjs +282 -59
- package/js/economy/legal.mjs +12 -6
- package/js/economy/live.mjs +62 -20
- package/js/economy/model.mjs +320 -97
- package/js/economy/plan.mjs +1022 -203
- package/js/economy/review.mjs +760 -0
- package/js/economy/schema.json +134 -372
- package/js/economy/secrets.mjs +17 -7
- package/js/economy/simulate.mjs +771 -178
- package/js/economy/trust.mjs +15 -0
- package/js/economy/txsize.mjs +15 -3
- package/js/economy/validate.mjs +61 -24
- package/js/index.js +1 -0
- package/js/mcp/server.mjs +43 -15
- package/js/mcp/tools.mjs +318 -80
- package/js/play/index.js +1960 -461
- package/js/play/keys.js +35 -9
- package/js/results/index.js +225 -32
- package/js/results/server.js +230 -0
- package/js/results/wire.js +64 -16
- package/package.json +6 -4
- package/release.json +5 -5
- package/skills/xsolana-economy/SKILL.md +52 -22
- package/skills/xsolana-economy/references/after-launch.md +32 -21
- package/skills/xsolana-economy/references/apps.md +47 -36
- package/skills/xsolana-economy/references/sentence-to-spec.md +149 -129
- package/skills/xsolana-economy/references/telling-the-person.md +15 -6
- package/skills/xsolana-economy/references/trust-and-cheating.md +126 -0
- package/types/chain/core.d.ts +1 -1
- package/types/chain/lookup.d.ts +1 -1
- package/types/client/accounts.d.ts +24 -3
- package/types/client/grant.d.ts +2 -2
- package/types/client/idl/loot.d.ts +22 -0
- package/types/client/ix.d.ts +54 -45
- package/types/client/pda.d.ts +3 -1
- package/types/economy/cost.d.mts +1 -1
- package/types/economy/explain.d.mts +3 -0
- package/types/economy/guide.d.mts +5 -0
- package/types/economy/model.d.mts +48 -3
- package/types/economy/plan.d.mts +44 -2
- package/types/economy/review.d.mts +45 -0
- package/types/economy/trust.d.mts +8 -0
- package/types/index.d.ts +1 -0
- package/types/mcp/tools.d.mts +62 -0
- package/types/play/index.d.ts +14 -25
- package/types/results/index.d.ts +8 -7
- package/types/results/server.d.ts +126 -0
- package/types/results/wire.d.ts +1 -1
package/js/approve/page.html
CHANGED
|
@@ -1,86 +1,225 @@
|
|
|
1
1
|
<!doctype html>
|
|
2
2
|
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8"
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1"
|
|
6
|
-
<meta name="xsolana-token" content="__TOKEN__"
|
|
7
|
-
<title>Approve going live · loot</title>
|
|
8
|
-
<style>
|
|
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
|
-
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<meta name="xsolana-token" content="__TOKEN__" />
|
|
7
|
+
<title>Approve going live · loot</title>
|
|
8
|
+
<style>
|
|
9
|
+
:root {
|
|
10
|
+
--bg: #f4f5f7;
|
|
11
|
+
--panel: #ffffff;
|
|
12
|
+
--ink: #14171f;
|
|
13
|
+
--muted: #5b6372;
|
|
14
|
+
--line: #dfe2e8;
|
|
15
|
+
--accent: #3446d6;
|
|
16
|
+
--ok: #1d7f4a;
|
|
17
|
+
--bad: #b42318;
|
|
18
|
+
--warn: #8a5a00;
|
|
19
|
+
--mono: ui-monospace, 'SFMono-Regular', Menlo, monospace;
|
|
20
|
+
}
|
|
21
|
+
@media (prefers-color-scheme: dark) {
|
|
22
|
+
:root {
|
|
23
|
+
--bg: #0f1218;
|
|
24
|
+
--panel: #171b23;
|
|
25
|
+
--ink: #e8ebf2;
|
|
26
|
+
--muted: #9aa3b5;
|
|
27
|
+
--line: #2a303c;
|
|
28
|
+
--accent: #8394ff;
|
|
29
|
+
--ok: #4cc47f;
|
|
30
|
+
--bad: #ff7a6b;
|
|
31
|
+
--warn: #e8b04a;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
* {
|
|
35
|
+
box-sizing: border-box;
|
|
36
|
+
}
|
|
37
|
+
body {
|
|
38
|
+
margin: 0;
|
|
39
|
+
background: var(--bg);
|
|
40
|
+
color: var(--ink);
|
|
41
|
+
font:
|
|
42
|
+
15px/1.55 system-ui,
|
|
43
|
+
-apple-system,
|
|
44
|
+
'Segoe UI',
|
|
45
|
+
sans-serif;
|
|
46
|
+
padding: 32px max(16px, env(safe-area-inset-left));
|
|
47
|
+
}
|
|
48
|
+
main {
|
|
49
|
+
max-width: 720px;
|
|
50
|
+
margin: 0 auto;
|
|
51
|
+
display: flex;
|
|
52
|
+
flex-direction: column;
|
|
53
|
+
gap: 20px;
|
|
54
|
+
}
|
|
55
|
+
h1 {
|
|
56
|
+
font-size: 1.5rem;
|
|
57
|
+
margin: 0;
|
|
58
|
+
}
|
|
59
|
+
.muted {
|
|
60
|
+
color: var(--muted);
|
|
61
|
+
}
|
|
62
|
+
.panel {
|
|
63
|
+
background: var(--panel);
|
|
64
|
+
border: 1px solid var(--line);
|
|
65
|
+
border-radius: 10px;
|
|
66
|
+
padding: 18px 20px;
|
|
67
|
+
}
|
|
68
|
+
.row {
|
|
69
|
+
display: flex;
|
|
70
|
+
flex-wrap: wrap;
|
|
71
|
+
gap: 10px;
|
|
72
|
+
align-items: center;
|
|
73
|
+
}
|
|
74
|
+
button {
|
|
75
|
+
font: inherit;
|
|
76
|
+
padding: 9px 16px;
|
|
77
|
+
border-radius: 8px;
|
|
78
|
+
border: 1px solid var(--accent);
|
|
79
|
+
background: var(--accent);
|
|
80
|
+
color: #fff;
|
|
81
|
+
cursor: pointer;
|
|
82
|
+
}
|
|
83
|
+
button.ghost {
|
|
84
|
+
background: transparent;
|
|
85
|
+
color: var(--accent);
|
|
86
|
+
}
|
|
87
|
+
button:disabled {
|
|
88
|
+
opacity: 0.5;
|
|
89
|
+
cursor: default;
|
|
90
|
+
}
|
|
91
|
+
button:focus-visible {
|
|
92
|
+
outline: 2px solid var(--accent);
|
|
93
|
+
outline-offset: 2px;
|
|
94
|
+
}
|
|
95
|
+
ol {
|
|
96
|
+
margin: 0;
|
|
97
|
+
padding-left: 22px;
|
|
98
|
+
display: flex;
|
|
99
|
+
flex-direction: column;
|
|
100
|
+
gap: 6px;
|
|
101
|
+
}
|
|
102
|
+
li.done {
|
|
103
|
+
color: var(--muted);
|
|
104
|
+
text-decoration: line-through;
|
|
105
|
+
text-decoration-color: var(--ok);
|
|
106
|
+
}
|
|
107
|
+
li .who {
|
|
108
|
+
font-size: 0.8rem;
|
|
109
|
+
color: var(--warn);
|
|
110
|
+
margin-left: 6px;
|
|
111
|
+
}
|
|
112
|
+
li .details {
|
|
113
|
+
margin: 4px 0 2px;
|
|
114
|
+
padding-left: 18px;
|
|
115
|
+
font-size: 0.85rem;
|
|
116
|
+
overflow-wrap: anywhere;
|
|
117
|
+
}
|
|
118
|
+
code,
|
|
119
|
+
.mono {
|
|
120
|
+
font-family: var(--mono);
|
|
121
|
+
font-size: 0.85em;
|
|
122
|
+
overflow-wrap: anywhere;
|
|
123
|
+
}
|
|
124
|
+
#log {
|
|
125
|
+
font-family: var(--mono);
|
|
126
|
+
font-size: 0.82rem;
|
|
127
|
+
white-space: pre-wrap;
|
|
128
|
+
margin: 0;
|
|
129
|
+
max-height: 220px;
|
|
130
|
+
overflow: auto;
|
|
131
|
+
}
|
|
132
|
+
.ok {
|
|
133
|
+
color: var(--ok);
|
|
134
|
+
}
|
|
135
|
+
.bad {
|
|
136
|
+
color: var(--bad);
|
|
137
|
+
}
|
|
138
|
+
ul.warn {
|
|
139
|
+
margin: 0;
|
|
140
|
+
padding-left: 18px;
|
|
141
|
+
color: var(--warn);
|
|
142
|
+
font-size: 0.9rem;
|
|
143
|
+
}
|
|
144
|
+
.flush {
|
|
145
|
+
margin: 0;
|
|
146
|
+
}
|
|
147
|
+
.note {
|
|
148
|
+
margin: 10px 0 0;
|
|
149
|
+
}
|
|
150
|
+
.spread {
|
|
151
|
+
justify-content: space-between;
|
|
152
|
+
}
|
|
153
|
+
.gap {
|
|
154
|
+
margin-top: 12px;
|
|
155
|
+
}
|
|
156
|
+
#wallets {
|
|
157
|
+
margin-top: 10px;
|
|
158
|
+
}
|
|
159
|
+
</style>
|
|
160
|
+
</head>
|
|
161
|
+
<body>
|
|
162
|
+
<main>
|
|
163
|
+
<header>
|
|
164
|
+
<p class="muted mono flush">loot · devnet · runs on your computer only</p>
|
|
165
|
+
<h1 id="title">Approve going live</h1>
|
|
166
|
+
<p class="muted" id="sub">Loading…</p>
|
|
167
|
+
</header>
|
|
41
168
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
169
|
+
<section class="panel">
|
|
170
|
+
<div class="row">
|
|
171
|
+
<button id="connect">Connect a wallet</button>
|
|
172
|
+
<span id="wallet" class="mono muted"></span>
|
|
173
|
+
</div>
|
|
174
|
+
<div class="row" id="wallets" hidden></div>
|
|
175
|
+
<p class="muted note">
|
|
176
|
+
Set the wallet to <b>devnet</b>. Each approval shows you the transactions before you sign; nothing leaves your
|
|
177
|
+
wallet without it. Nobody else ever sees your key.
|
|
178
|
+
</p>
|
|
179
|
+
</section>
|
|
50
180
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
181
|
+
<section class="panel">
|
|
182
|
+
<div class="row spread">
|
|
183
|
+
<b>What going live does</b>
|
|
184
|
+
<span class="muted" id="cost"></span>
|
|
185
|
+
</div>
|
|
186
|
+
<ol id="steps" class="gap"></ol>
|
|
187
|
+
</section>
|
|
58
188
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
189
|
+
<section class="panel" id="legalbox">
|
|
190
|
+
<b>Read this first</b>
|
|
191
|
+
<ul class="gap" id="terms">
|
|
192
|
+
<li>
|
|
193
|
+
The loot program has not been independently audited. It and these tools come as is, with no warranty and no
|
|
194
|
+
guarantee that they are safe: what is sent to the program can be lost. You use them at your own risk.
|
|
195
|
+
</li>
|
|
196
|
+
<li>Transactions can't be undone. You are responsible for every one you approve.</li>
|
|
197
|
+
<li>
|
|
198
|
+
Making sure your economy is legal everywhere it is offered is up to you. Nothing here is legal or financial
|
|
199
|
+
advice.
|
|
200
|
+
</li>
|
|
201
|
+
</ul>
|
|
202
|
+
<ul class="warn gap" id="legal" hidden></ul>
|
|
203
|
+
<p class="muted note">The full text is in DISCLAIMER.md, in the package.</p>
|
|
204
|
+
<label class="row gap"
|
|
205
|
+
><input type="checkbox" id="legalok" /> I have read this and accept it. Devnet is not real money; real money
|
|
206
|
+
needs every point above answered first.</label
|
|
207
|
+
>
|
|
208
|
+
</section>
|
|
70
209
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
210
|
+
<section class="panel" id="warnbox" hidden>
|
|
211
|
+
<b>Before you approve</b>
|
|
212
|
+
<ul class="warn" id="warnings"></ul>
|
|
213
|
+
</section>
|
|
75
214
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
</main>
|
|
84
|
-
<script type="module" src="/js/approve/page.js"></script>
|
|
85
|
-
</body>
|
|
215
|
+
<section class="panel">
|
|
216
|
+
<div class="row">
|
|
217
|
+
<button id="approve" disabled>Approve the next batch</button>
|
|
218
|
+
<span id="status" class="muted"></span>
|
|
219
|
+
</div>
|
|
220
|
+
<pre id="log"></pre>
|
|
221
|
+
</section>
|
|
222
|
+
</main>
|
|
223
|
+
<script type="module" src="/js/approve/page.js"></script>
|
|
224
|
+
</body>
|
|
86
225
|
</html>
|
package/js/approve/page.js
CHANGED
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
import { fromBase64, toBase64 } from '/js/chain/bytes.js';
|
|
7
7
|
|
|
8
8
|
const CHAIN = 'solana:devnet';
|
|
9
|
-
const $ =
|
|
9
|
+
const $ = id => document.getElementById(id);
|
|
10
10
|
|
|
11
11
|
function walletRegistry() {
|
|
12
12
|
const wallets = new Set();
|
|
13
13
|
const api = Object.freeze({
|
|
14
14
|
register: (...ws) => {
|
|
15
15
|
for (const w of ws) if (w?.features) wallets.add(w);
|
|
16
|
-
return () => ws.forEach(
|
|
16
|
+
return () => ws.forEach(w => wallets.delete(w));
|
|
17
17
|
},
|
|
18
18
|
});
|
|
19
19
|
window.addEventListener('wallet-standard:register-wallet', ({ detail }) => {
|
|
@@ -24,7 +24,13 @@ function walletRegistry() {
|
|
|
24
24
|
}
|
|
25
25
|
});
|
|
26
26
|
window.dispatchEvent(Object.assign(new Event('wallet-standard:app-ready'), { detail: api }));
|
|
27
|
-
return () =>
|
|
27
|
+
return () =>
|
|
28
|
+
[...wallets].filter(
|
|
29
|
+
w =>
|
|
30
|
+
(w.chains ?? []).some(c => String(c).startsWith('solana:')) &&
|
|
31
|
+
w.features['standard:connect'] &&
|
|
32
|
+
w.features['solana:signTransaction'],
|
|
33
|
+
);
|
|
28
34
|
}
|
|
29
35
|
|
|
30
36
|
export function approvalPage(token) {
|
|
@@ -36,7 +42,11 @@ export function approvalPage(token) {
|
|
|
36
42
|
// While a batch is out for signing or sending, nothing re-enables Approve (a state refresh landing mid-way did).
|
|
37
43
|
let busy = false;
|
|
38
44
|
const api = async (path, body) => {
|
|
39
|
-
const r = await fetch(path, {
|
|
45
|
+
const r = await fetch(path, {
|
|
46
|
+
method: body ? 'POST' : 'GET',
|
|
47
|
+
headers: { 'x-xsolana-token': token, 'content-type': 'application/json' },
|
|
48
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
49
|
+
});
|
|
40
50
|
const data = await r.json().catch(() => ({}));
|
|
41
51
|
if (!r.ok) throw new Error(data.error ?? `HTTP ${r.status}`);
|
|
42
52
|
return data;
|
|
@@ -59,7 +69,7 @@ export function approvalPage(token) {
|
|
|
59
69
|
$('cost').textContent = state.setupSol !== null && state.mode === 'new' ? `about ${state.setupSol} SOL` : '';
|
|
60
70
|
const steps = $('steps');
|
|
61
71
|
steps.replaceChildren(
|
|
62
|
-
...[...state.landed.map(
|
|
72
|
+
...[...state.landed.map(l => ({ ...l, done: true })), ...state.steps].map(s => {
|
|
63
73
|
const li = document.createElement('li');
|
|
64
74
|
li.textContent = s.says;
|
|
65
75
|
if (s.done) li.className = 'done';
|
|
@@ -73,7 +83,7 @@ export function approvalPage(token) {
|
|
|
73
83
|
if (s.details?.length) {
|
|
74
84
|
const ul = document.createElement('ul');
|
|
75
85
|
ul.className = 'details';
|
|
76
|
-
ul.append(...s.details.map(
|
|
86
|
+
ul.append(...s.details.map(d => Object.assign(document.createElement('li'), { textContent: d })));
|
|
77
87
|
li.append(ul);
|
|
78
88
|
}
|
|
79
89
|
return li;
|
|
@@ -81,17 +91,27 @@ export function approvalPage(token) {
|
|
|
81
91
|
);
|
|
82
92
|
const warnings = state.warnings ?? [];
|
|
83
93
|
$('warnbox').hidden = !warnings.length || state.done;
|
|
84
|
-
$('warnings').replaceChildren(
|
|
94
|
+
$('warnings').replaceChildren(
|
|
95
|
+
...warnings.map(w => Object.assign(document.createElement('li'), { textContent: w })),
|
|
96
|
+
);
|
|
85
97
|
// The notice (no warranty, at the owner's own risk, legal where it runs) and this economy's legal flags, in a
|
|
86
98
|
// block of their own whatever the agent said: Approve waits until the owner ticks that they read them.
|
|
87
99
|
const legal = state.legal ?? [];
|
|
88
100
|
$('legalbox').hidden = Boolean(state.done);
|
|
89
101
|
$('legal').hidden = !legal.length;
|
|
90
|
-
$('legal').replaceChildren(...legal.map(
|
|
102
|
+
$('legal').replaceChildren(...legal.map(f => Object.assign(document.createElement('li'), { textContent: f.says })));
|
|
91
103
|
const unread = !$('legalok').checked;
|
|
92
104
|
// A live section the chain does not bear out: nothing can be approved.
|
|
93
105
|
$('approve').disabled = state.done || !account || busy || Boolean(state.refused) || unread;
|
|
94
|
-
$('status').textContent = state.done
|
|
106
|
+
$('status').textContent = state.done
|
|
107
|
+
? 'Done.'
|
|
108
|
+
: state.refused
|
|
109
|
+
? 'Nothing can be approved: see above.'
|
|
110
|
+
: !account
|
|
111
|
+
? 'Connect a wallet first.'
|
|
112
|
+
: unread
|
|
113
|
+
? 'Read the notice above and tick that you have.'
|
|
114
|
+
: `${state.remaining} transaction${state.remaining === 1 ? '' : 's'} to go.`;
|
|
95
115
|
}
|
|
96
116
|
|
|
97
117
|
async function refresh() {
|
|
@@ -147,7 +167,10 @@ export function approvalPage(token) {
|
|
|
147
167
|
if (busy) return;
|
|
148
168
|
const found = list();
|
|
149
169
|
if (!found.length) {
|
|
150
|
-
log(
|
|
170
|
+
log(
|
|
171
|
+
'No Solana wallet in this browser. Install one (Phantom, Solflare, Backpack), set it to devnet, then reload.',
|
|
172
|
+
'bad',
|
|
173
|
+
);
|
|
151
174
|
return;
|
|
152
175
|
}
|
|
153
176
|
if (found.length === 1) {
|
|
@@ -157,8 +180,11 @@ export function approvalPage(token) {
|
|
|
157
180
|
const box = $('wallets');
|
|
158
181
|
box.replaceChildren(
|
|
159
182
|
Object.assign(document.createElement('span'), { className: 'muted', textContent: 'Pick a wallet:' }),
|
|
160
|
-
...found.map(
|
|
161
|
-
const button = Object.assign(document.createElement('button'), {
|
|
183
|
+
...found.map(w => {
|
|
184
|
+
const button = Object.assign(document.createElement('button'), {
|
|
185
|
+
className: 'ghost',
|
|
186
|
+
textContent: w === wallet ? `${w.name} (connected)` : w.name,
|
|
187
|
+
});
|
|
162
188
|
button.addEventListener('click', () => connectTo(w));
|
|
163
189
|
return button;
|
|
164
190
|
}),
|
|
@@ -177,11 +203,13 @@ export function approvalPage(token) {
|
|
|
177
203
|
try {
|
|
178
204
|
const batch = await api('/api/prepare', { wallet: signer.address });
|
|
179
205
|
const details = (batch.details ?? []).flat();
|
|
180
|
-
log(
|
|
206
|
+
log(
|
|
207
|
+
`Asking your wallet to approve ${batch.transactions.length} transaction${batch.transactions.length === 1 ? '' : 's'} in the project ${batch.project}: ${batch.says.join(' | ')}${details.length ? `. ${details.join('. ')}` : ''}`,
|
|
208
|
+
);
|
|
181
209
|
const sign = signingWallet.features['solana:signTransaction'];
|
|
182
|
-
const inputs = batch.transactions.map(
|
|
210
|
+
const inputs = batch.transactions.map(t => ({ account: signer, transaction: fromBase64(t), chain: CHAIN }));
|
|
183
211
|
const outputs = await sign.signTransaction(...inputs);
|
|
184
|
-
const signed = outputs.map(
|
|
212
|
+
const signed = outputs.map(o => toBase64(o.signedTransaction));
|
|
185
213
|
$('status').textContent = 'Sending and confirming…';
|
|
186
214
|
const state = await api('/api/complete', { wallet: signer.address, id: batch.id, transactions: signed });
|
|
187
215
|
log(`Landed: ${state.signatures.slice(-batch.transactions.length).join(', ')}`, 'ok');
|