@silvana-one/mina-utils 0.2.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.
Files changed (174) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +1 -0
  3. package/dist/node/config.d.ts +4 -0
  4. package/dist/node/config.js +5 -0
  5. package/dist/node/config.js.map +1 -0
  6. package/dist/node/index.cjs +1325 -0
  7. package/dist/node/index.d.ts +6 -0
  8. package/dist/node/index.js +7 -0
  9. package/dist/node/index.js.map +1 -0
  10. package/dist/node/networks.d.ts +32 -0
  11. package/dist/node/networks.js +81 -0
  12. package/dist/node/networks.js.map +1 -0
  13. package/dist/node/storage/index.d.ts +2 -0
  14. package/dist/node/storage/index.js +3 -0
  15. package/dist/node/storage/index.js.map +1 -0
  16. package/dist/node/storage/ipfs.d.ts +5 -0
  17. package/dist/node/storage/ipfs.js +16 -0
  18. package/dist/node/storage/ipfs.js.map +1 -0
  19. package/dist/node/storage/pinata.d.ts +6 -0
  20. package/dist/node/storage/pinata.js +40 -0
  21. package/dist/node/storage/pinata.js.map +1 -0
  22. package/dist/node/transactions/account.d.ts +4 -0
  23. package/dist/node/transactions/account.js +44 -0
  24. package/dist/node/transactions/account.js.map +1 -0
  25. package/dist/node/transactions/blockberry.d.ts +21 -0
  26. package/dist/node/transactions/blockberry.js +130 -0
  27. package/dist/node/transactions/blockberry.js.map +1 -0
  28. package/dist/node/transactions/chain.d.ts +1 -0
  29. package/dist/node/transactions/chain.js +2 -0
  30. package/dist/node/transactions/chain.js.map +1 -0
  31. package/dist/node/transactions/index.d.ts +8 -0
  32. package/dist/node/transactions/index.js +9 -0
  33. package/dist/node/transactions/index.js.map +1 -0
  34. package/dist/node/transactions/nonce.d.ts +17 -0
  35. package/dist/node/transactions/nonce.js +91 -0
  36. package/dist/node/transactions/nonce.js.map +1 -0
  37. package/dist/node/transactions/send.d.ts +31 -0
  38. package/dist/node/transactions/send.js +154 -0
  39. package/dist/node/transactions/send.js.map +1 -0
  40. package/dist/node/transactions/tiny-contract.d.ts +5 -0
  41. package/dist/node/transactions/tiny-contract.js +22 -0
  42. package/dist/node/transactions/tiny-contract.js.map +1 -0
  43. package/dist/node/transactions/transaction.d.ts +21 -0
  44. package/dist/node/transactions/transaction.js +222 -0
  45. package/dist/node/transactions/transaction.js.map +1 -0
  46. package/dist/node/transactions/txstatus.d.ts +8 -0
  47. package/dist/node/transactions/txstatus.js +16 -0
  48. package/dist/node/transactions/txstatus.js.map +1 -0
  49. package/dist/node/utils/base64-field.d.ts +5 -0
  50. package/dist/node/utils/base64-field.js +29 -0
  51. package/dist/node/utils/base64-field.js.map +1 -0
  52. package/dist/node/utils/base64.d.ts +6 -0
  53. package/dist/node/utils/base64.js +83 -0
  54. package/dist/node/utils/base64.js.map +1 -0
  55. package/dist/node/utils/fee.d.ts +6 -0
  56. package/dist/node/utils/fee.js +11 -0
  57. package/dist/node/utils/fee.js.map +1 -0
  58. package/dist/node/utils/fetch.d.ts +44 -0
  59. package/dist/node/utils/fetch.js +101 -0
  60. package/dist/node/utils/fetch.js.map +1 -0
  61. package/dist/node/utils/fields.d.ts +13 -0
  62. package/dist/node/utils/fields.js +39 -0
  63. package/dist/node/utils/fields.js.map +1 -0
  64. package/dist/node/utils/index.d.ts +8 -0
  65. package/dist/node/utils/index.js +9 -0
  66. package/dist/node/utils/index.js.map +1 -0
  67. package/dist/node/utils/indexed-map.d.ts +32 -0
  68. package/dist/node/utils/indexed-map.js +124 -0
  69. package/dist/node/utils/indexed-map.js.map +1 -0
  70. package/dist/node/utils/mina.d.ts +39 -0
  71. package/dist/node/utils/mina.js +123 -0
  72. package/dist/node/utils/mina.js.map +1 -0
  73. package/dist/node/utils/utils.d.ts +8 -0
  74. package/dist/node/utils/utils.js +61 -0
  75. package/dist/node/utils/utils.js.map +1 -0
  76. package/dist/tsconfig.tsbuildinfo +1 -0
  77. package/dist/tsconfig.web.tsbuildinfo +1 -0
  78. package/dist/web/config.d.ts +4 -0
  79. package/dist/web/config.js +5 -0
  80. package/dist/web/config.js.map +1 -0
  81. package/dist/web/index.d.ts +6 -0
  82. package/dist/web/index.js +7 -0
  83. package/dist/web/index.js.map +1 -0
  84. package/dist/web/networks.d.ts +32 -0
  85. package/dist/web/networks.js +81 -0
  86. package/dist/web/networks.js.map +1 -0
  87. package/dist/web/storage/index.d.ts +2 -0
  88. package/dist/web/storage/index.js +3 -0
  89. package/dist/web/storage/index.js.map +1 -0
  90. package/dist/web/storage/ipfs.d.ts +5 -0
  91. package/dist/web/storage/ipfs.js +16 -0
  92. package/dist/web/storage/ipfs.js.map +1 -0
  93. package/dist/web/storage/pinata.d.ts +6 -0
  94. package/dist/web/storage/pinata.js +40 -0
  95. package/dist/web/storage/pinata.js.map +1 -0
  96. package/dist/web/transactions/account.d.ts +4 -0
  97. package/dist/web/transactions/account.js +44 -0
  98. package/dist/web/transactions/account.js.map +1 -0
  99. package/dist/web/transactions/blockberry.d.ts +21 -0
  100. package/dist/web/transactions/blockberry.js +130 -0
  101. package/dist/web/transactions/blockberry.js.map +1 -0
  102. package/dist/web/transactions/chain.d.ts +1 -0
  103. package/dist/web/transactions/chain.js +2 -0
  104. package/dist/web/transactions/chain.js.map +1 -0
  105. package/dist/web/transactions/index.d.ts +8 -0
  106. package/dist/web/transactions/index.js +9 -0
  107. package/dist/web/transactions/index.js.map +1 -0
  108. package/dist/web/transactions/nonce.d.ts +17 -0
  109. package/dist/web/transactions/nonce.js +91 -0
  110. package/dist/web/transactions/nonce.js.map +1 -0
  111. package/dist/web/transactions/send.d.ts +31 -0
  112. package/dist/web/transactions/send.js +154 -0
  113. package/dist/web/transactions/send.js.map +1 -0
  114. package/dist/web/transactions/tiny-contract.d.ts +5 -0
  115. package/dist/web/transactions/tiny-contract.js +22 -0
  116. package/dist/web/transactions/tiny-contract.js.map +1 -0
  117. package/dist/web/transactions/transaction.d.ts +21 -0
  118. package/dist/web/transactions/transaction.js +222 -0
  119. package/dist/web/transactions/transaction.js.map +1 -0
  120. package/dist/web/transactions/txstatus.d.ts +8 -0
  121. package/dist/web/transactions/txstatus.js +16 -0
  122. package/dist/web/transactions/txstatus.js.map +1 -0
  123. package/dist/web/utils/base64-field.d.ts +5 -0
  124. package/dist/web/utils/base64-field.js +29 -0
  125. package/dist/web/utils/base64-field.js.map +1 -0
  126. package/dist/web/utils/base64.d.ts +6 -0
  127. package/dist/web/utils/base64.js +83 -0
  128. package/dist/web/utils/base64.js.map +1 -0
  129. package/dist/web/utils/fee.d.ts +6 -0
  130. package/dist/web/utils/fee.js +11 -0
  131. package/dist/web/utils/fee.js.map +1 -0
  132. package/dist/web/utils/fetch.d.ts +44 -0
  133. package/dist/web/utils/fetch.js +101 -0
  134. package/dist/web/utils/fetch.js.map +1 -0
  135. package/dist/web/utils/fields.d.ts +13 -0
  136. package/dist/web/utils/fields.js +39 -0
  137. package/dist/web/utils/fields.js.map +1 -0
  138. package/dist/web/utils/index.d.ts +8 -0
  139. package/dist/web/utils/index.js +9 -0
  140. package/dist/web/utils/index.js.map +1 -0
  141. package/dist/web/utils/indexed-map.d.ts +32 -0
  142. package/dist/web/utils/indexed-map.js +124 -0
  143. package/dist/web/utils/indexed-map.js.map +1 -0
  144. package/dist/web/utils/mina.d.ts +39 -0
  145. package/dist/web/utils/mina.js +123 -0
  146. package/dist/web/utils/mina.js.map +1 -0
  147. package/dist/web/utils/utils.d.ts +8 -0
  148. package/dist/web/utils/utils.js +61 -0
  149. package/dist/web/utils/utils.js.map +1 -0
  150. package/package.json +62 -0
  151. package/src/config.ts +5 -0
  152. package/src/index.ts +6 -0
  153. package/src/networks.ts +130 -0
  154. package/src/storage/index.ts +2 -0
  155. package/src/storage/ipfs.ts +20 -0
  156. package/src/storage/pinata.ts +56 -0
  157. package/src/transactions/account.ts +57 -0
  158. package/src/transactions/blockberry.ts +198 -0
  159. package/src/transactions/chain.ts +1 -0
  160. package/src/transactions/index.ts +8 -0
  161. package/src/transactions/nonce.ts +121 -0
  162. package/src/transactions/send.ts +228 -0
  163. package/src/transactions/tiny-contract.ts +9 -0
  164. package/src/transactions/transaction.ts +319 -0
  165. package/src/transactions/txstatus.ts +26 -0
  166. package/src/utils/base64-field.ts +34 -0
  167. package/src/utils/base64.ts +87 -0
  168. package/src/utils/fee.ts +11 -0
  169. package/src/utils/fetch.ts +130 -0
  170. package/src/utils/fields.ts +39 -0
  171. package/src/utils/index.ts +8 -0
  172. package/src/utils/indexed-map.ts +170 -0
  173. package/src/utils/mina.ts +171 -0
  174. package/src/utils/utils.ts +79 -0
@@ -0,0 +1,228 @@
1
+ import { checkZkappTransaction } from "o1js";
2
+ import { Mina } from "o1js";
3
+ import { blockchain } from "../networks.js";
4
+ import { sleep } from "../utils/utils.js";
5
+ import { fetchMinaAccount } from "../utils/fetch.js";
6
+ import { getCurrentNetwork } from "../utils/mina.js";
7
+
8
+ /**
9
+ * The function `sendTx` sends a transaction, checks account updates, and waits for
10
+ * confirmation on the blockchain.
11
+ * @param params The parameters object
12
+ * @param params.tx The transaction to send
13
+ * @param params.description A description of the transaction
14
+ * @param params.verbose Whether to log verbose information
15
+ * @param params.wait Whether to wait for the transaction to be included in a block
16
+ * @param params.chain The blockchain to send the transaction on
17
+ * @returns The `sendTx` function returns a `Mina.IncludedTransaction`, `Mina.PendingTransaction`,
18
+ * `Mina.RejectedTransaction`, or `undefined` if there was an error during the process.
19
+ */
20
+ export async function sendTx(params: {
21
+ tx: Mina.Transaction<false, true> | Mina.Transaction<true, true>;
22
+ description?: string;
23
+ retry?: number;
24
+ verbose?: boolean;
25
+ wait?: boolean;
26
+ chain?: blockchain;
27
+ delay?: number;
28
+ }): Promise<
29
+ | Mina.IncludedTransaction
30
+ | Mina.PendingTransaction
31
+ | Mina.RejectedTransaction
32
+ | undefined
33
+ > {
34
+ const {
35
+ tx,
36
+ description = "",
37
+ verbose = true,
38
+ wait = true,
39
+ chain = getCurrentNetwork().network.chainId,
40
+ delay = chain === "zeko" || chain === "lightnet" ? 5000 : 60000,
41
+ retry = 30,
42
+ } = params;
43
+ // flatten accountUpdates
44
+ const accountUpdates = JSON.parse(tx.toJSON()).accountUpdates;
45
+ const auCount: {
46
+ publicKey: string;
47
+ tokenId: string | undefined;
48
+ count: number;
49
+ }[] = [];
50
+ let proofAuthorizationCount = 0;
51
+ // Calculate the number of account updates for each { publicKey, tokenId }
52
+ for (const au of accountUpdates) {
53
+ const { publicKey, tokenId, authorizationKind } = au.body;
54
+ if (au.authorization.proof) {
55
+ proofAuthorizationCount++;
56
+ if (authorizationKind.isProved === false)
57
+ console.error("Proof authorization exists but isProved is false");
58
+ } else if (authorizationKind.isProved === true)
59
+ console.error("isProved is true but no proof authorization");
60
+ const index = auCount.findIndex(
61
+ (item) => item.publicKey === publicKey && item.tokenId === tokenId
62
+ );
63
+ if (index === -1) auCount.push({ publicKey, tokenId, count: 1 });
64
+ else auCount[index].count++;
65
+ }
66
+ if (verbose)
67
+ console.log(
68
+ `Account updates for ${description ?? "tx"}: ${
69
+ auCount.length
70
+ }, proof authorizations: ${proofAuthorizationCount}`
71
+ );
72
+ for (const au of auCount) {
73
+ if (au.count > 1)
74
+ if (verbose)
75
+ console.log(
76
+ `DUPLICATE AU ${description ?? ""}: ${au.publicKey} ${
77
+ au.tokenId !== "wSHV2S4qX9jFsLjQo8r1BsMLH2ZRKsZx6EJd1sbozGPieEC4Jf"
78
+ ? "tokenId: " + au.tokenId
79
+ : ""
80
+ } count: ${au.count}`
81
+ );
82
+ }
83
+ try {
84
+ let txSent;
85
+ let sent = false;
86
+ let attempt = 1;
87
+ while (!sent && attempt <= retry) {
88
+ txSent = await tx.safeSend();
89
+ if (txSent.status === "pending") {
90
+ sent = true;
91
+ if (verbose)
92
+ console.log(
93
+ `${description ?? ""} tx sent: hash: ${txSent.hash} status: ${
94
+ txSent.status
95
+ }`
96
+ );
97
+ } else if (chain !== "local") {
98
+ attempt++;
99
+ console.error(
100
+ `${description} tx NOT sent: hash: ${txSent?.hash} status: ${
101
+ txSent?.status
102
+ }, errors: ${String(txSent.errors ?? "")}`
103
+ );
104
+ if (verbose) console.log(`Retrying ${chain} tx, retry:`, attempt);
105
+ await sleep(10000 * attempt);
106
+ } else attempt = retry + 1; // local chain - do not retry
107
+ }
108
+ if (txSent === undefined) throw new Error("txSent is undefined");
109
+ if ((txSent.errors?.length ?? 0) > 0) {
110
+ console.error(
111
+ `${description ?? ""} tx error: hash: ${txSent.hash} status: ${
112
+ txSent.status
113
+ } errors: ${String(txSent.errors ?? "")}`
114
+ );
115
+ }
116
+
117
+ if (txSent.status === "pending" && wait !== false && chain !== "zeko") {
118
+ if (verbose) console.log(`Waiting for tx inclusion...`);
119
+ let txIncluded = await txSent.safeWait();
120
+ if (txIncluded.status !== "included") {
121
+ console.error(
122
+ `${description ?? ""} tx NOT included into block: hash: ${
123
+ txIncluded.hash
124
+ } status: ${txIncluded.status}, errors: ${String(
125
+ txIncluded.errors ?? ""
126
+ )}`
127
+ );
128
+ }
129
+ if (chain !== "local") {
130
+ // we still wait for the tx to be included in the block by checking the nonce
131
+ // even in the case of tx NOT included
132
+ // because the tx might still be included in the block in the case of devnet instability
133
+ const { publicKey, nonce } = tx.transaction.feePayer.body;
134
+ const started = Date.now();
135
+ while (Date.now() - started < 1000 * 60 * 10) {
136
+ const newNonce = (
137
+ await fetchMinaAccount({
138
+ publicKey,
139
+ force: true,
140
+ })
141
+ ).account?.nonce;
142
+ if (
143
+ newNonce &&
144
+ Number(newNonce.toBigint()) > Number(nonce.toBigint())
145
+ ) {
146
+ let txIncluded = await txSent.safeWait();
147
+ if (txIncluded.status === "included") {
148
+ if (verbose)
149
+ console.log(
150
+ `${description ?? ""} tx included into block: hash: ${
151
+ txIncluded.hash
152
+ } status: ${txIncluded.status}`
153
+ );
154
+ if (delay > 0) await sleep(delay);
155
+ return txIncluded;
156
+ } else if (txIncluded.status === "rejected") {
157
+ // We never should see this error as the nonce is updated (except when tx failed due to a preconditions),
158
+ // so we retry to check if the tx is included
159
+ console.error(
160
+ `tx rejected: ${chain}: hash: ${txIncluded.hash} status: ${txIncluded.status} errors: ${txIncluded.errors}`
161
+ );
162
+ await sleep(30000);
163
+ txIncluded = await txSent.safeWait();
164
+ if (txIncluded.status === "included") {
165
+ if (verbose)
166
+ console.log(
167
+ `${description ?? ""} tx included into block: hash: ${
168
+ txIncluded.hash
169
+ } status: ${txIncluded.status}`
170
+ );
171
+ if (delay > 0) await sleep(delay);
172
+ return txIncluded;
173
+ }
174
+ console.error(
175
+ `tx failed: ${chain}: hash: ${txIncluded.hash} status: ${txIncluded.status} errors: ${txIncluded.errors}`
176
+ );
177
+ return txIncluded;
178
+ }
179
+ }
180
+ if (verbose)
181
+ console.log(
182
+ `Waiting for ${chain} to update state for ${Math.floor(
183
+ (Date.now() - started) / 1000
184
+ )} sec...`
185
+ );
186
+ await sleep(30000);
187
+ }
188
+ // finally, if the tx is still not included, show an error
189
+ console.error(
190
+ `${chain} do not reflect nonce update for tx ${txIncluded.hash} with status ${txIncluded.status}`
191
+ );
192
+ }
193
+ if (verbose)
194
+ console.log(
195
+ `${description ?? ""} tx included into block: hash: ${
196
+ txIncluded.hash
197
+ } status: ${txIncluded.status}`
198
+ );
199
+ return txIncluded;
200
+ } else return txSent;
201
+ } catch (error) {
202
+ if (chain !== "zeko") console.error("Error sending tx", error);
203
+ }
204
+ }
205
+
206
+ export async function getTxStatusFast(params: {
207
+ hash: string;
208
+ chain?: blockchain;
209
+ }): Promise<{ success: boolean; result?: boolean; error?: string }> {
210
+ const { hash, chain = getCurrentNetwork().network.chainId } = params;
211
+ if (chain === "local" || chain === "zeko")
212
+ return { success: true, result: true };
213
+
214
+ try {
215
+ const txStatus = await checkZkappTransaction(hash);
216
+ return {
217
+ success: true,
218
+ result: txStatus?.success ?? false,
219
+ };
220
+ } catch (error: any) {
221
+ console.error(
222
+ "getTxStatusFast error while getting tx status - catch",
223
+ hash,
224
+ error
225
+ );
226
+ return { success: false, error: error?.message ?? "Cannot get tx status" };
227
+ }
228
+ }
@@ -0,0 +1,9 @@
1
+ import { Field, SmartContract, method, state, State } from "o1js";
2
+
3
+ export class TinyContract extends SmartContract {
4
+ @state(Field) value = State<Field>();
5
+
6
+ @method async setValue(value: Field) {
7
+ this.value.set(value);
8
+ }
9
+ }
@@ -0,0 +1,319 @@
1
+ import { Field, PublicKey, Transaction, Mina, UInt64 } from "o1js";
2
+ import { TransactionPayloads } from "@silvana-one/api";
3
+ import { fieldToBase64, fieldFromBase64 } from "../utils/base64-field.js";
4
+
5
+ export function createTransactionPayloads(
6
+ tx: Mina.Transaction<false, false> | Mina.Transaction<false, true>
7
+ ): TransactionPayloads {
8
+ const transaction = tx.toJSON();
9
+ const txJSON = JSON.parse(transaction);
10
+ const signedData = JSON.stringify({ zkappCommand: txJSON });
11
+ const proverPayload = serializeTransaction(tx);
12
+ const fee = tx.transaction.feePayer.body.fee.toJSON();
13
+ const sender = tx.transaction.feePayer.body.publicKey.toBase58();
14
+ const nonce = Number(tx.transaction.feePayer.body.nonce.toBigint());
15
+ const memo = tx.transaction.memo;
16
+ const minaSignerPayload = {
17
+ zkappCommand: txJSON,
18
+ feePayer: {
19
+ feePayer: sender,
20
+ fee,
21
+ nonce,
22
+ memo,
23
+ },
24
+ };
25
+ const walletPayload = {
26
+ transaction,
27
+ nonce,
28
+ onlySign: true,
29
+ feePayer: {
30
+ fee,
31
+ memo,
32
+ },
33
+ };
34
+
35
+ return {
36
+ sender,
37
+ nonce,
38
+ memo,
39
+ fee,
40
+ walletPayload,
41
+ minaSignerPayload,
42
+ proverPayload,
43
+ signedData,
44
+ transaction,
45
+ };
46
+ }
47
+
48
+ interface ForestSerialized {
49
+ length: number;
50
+ restoredItems?: number;
51
+ items: {
52
+ h?: string; // hash
53
+ p?: string; // previousHash
54
+ i?: number; // id
55
+ c?: string; // callData
56
+ }[];
57
+ }
58
+
59
+ export function transactionParams(
60
+ params:
61
+ | {
62
+ proverPayload: string;
63
+ signedData: string;
64
+ }
65
+ | TransactionPayloads
66
+ ): {
67
+ fee: UInt64;
68
+ sender: PublicKey;
69
+ nonce: number;
70
+ memo: string;
71
+ } {
72
+ const { proverPayload, signedData } = params;
73
+ const signedJson = JSON.parse(signedData);
74
+ const { sender, tx } = JSON.parse(proverPayload);
75
+ const transaction = Mina.Transaction.fromJSON(JSON.parse(tx));
76
+ const memo = transaction.transaction.memo;
77
+ return {
78
+ fee: UInt64.from(signedJson.zkappCommand.feePayer.body.fee),
79
+ sender: PublicKey.fromBase58(sender),
80
+ nonce: Number(signedJson.zkappCommand.feePayer.body.nonce),
81
+ memo,
82
+ };
83
+ }
84
+
85
+ export function parseTransactionPayloads(
86
+ params:
87
+ | {
88
+ proverPayload: string;
89
+ signedData: string;
90
+ txNew: Mina.Transaction<false, false> | Mina.Transaction<false, true>;
91
+ }
92
+ | {
93
+ payloads: TransactionPayloads;
94
+ txNew: Mina.Transaction<false, false> | Mina.Transaction<false, true>;
95
+ }
96
+ ): Transaction<false, true> {
97
+ const { txNew } = params;
98
+ const proverPayload =
99
+ "payloads" in params ? params.payloads.proverPayload : params.proverPayload;
100
+ const signedData =
101
+ "payloads" in params ? params.payloads.signedData : params.signedData;
102
+ const signedJson = JSON.parse(signedData);
103
+ const { tx, blindingValues, length, forestJSONs } = JSON.parse(proverPayload);
104
+ const transaction = Mina.Transaction.fromJSON(JSON.parse(tx));
105
+ const forests: ForestSerialized[] = forestJSONs.map(
106
+ (f: string) => JSON.parse(f) as ForestSerialized
107
+ );
108
+
109
+ if (length !== txNew.transaction.accountUpdates.length) {
110
+ throw new Error(
111
+ `New Transaction length mismatch: ${length} !== ${txNew.transaction.accountUpdates.length}`
112
+ );
113
+ }
114
+ if (length !== transaction.transaction.accountUpdates.length) {
115
+ throw new Error(
116
+ `Serialized Transaction length mismatch: ${length} !== ${transaction.transaction.accountUpdates.length}`
117
+ );
118
+ }
119
+ for (let i = 0; i < length; i++) {
120
+ transaction.transaction.accountUpdates[i].lazyAuthorization =
121
+ txNew.transaction.accountUpdates[i].lazyAuthorization;
122
+ if (blindingValues[i] !== "") {
123
+ if (
124
+ transaction.transaction.accountUpdates[i].lazyAuthorization ===
125
+ undefined ||
126
+ (transaction.transaction.accountUpdates[i].lazyAuthorization as any)
127
+ .blindingValue === undefined
128
+ ) {
129
+ throw new Error(
130
+ `Lazy authorization blinding value is undefined for item ${i}`
131
+ );
132
+ }
133
+ (
134
+ transaction.transaction.accountUpdates[i].lazyAuthorization as any
135
+ ).blindingValue = Field.fromJSON(blindingValues[i]);
136
+ }
137
+ if (forests[i].length > 0) {
138
+ if (
139
+ transaction.transaction.accountUpdates[i].lazyAuthorization ===
140
+ undefined ||
141
+ (transaction.transaction.accountUpdates[i].lazyAuthorization as any)
142
+ .args === undefined
143
+ ) {
144
+ throw new Error(`Lazy authorization args is undefined for item ${i}`);
145
+ }
146
+ deserializeLazyAuthorization(
147
+ (transaction.transaction.accountUpdates[i].lazyAuthorization as any)
148
+ .args,
149
+ forests[i]
150
+ );
151
+ if (forests[i].restoredItems !== forests[i].length) {
152
+ throw new Error(`Forest ${i} not fully restored`);
153
+ }
154
+ }
155
+ }
156
+ transaction.transaction.feePayer.authorization =
157
+ signedJson.zkappCommand.feePayer.authorization;
158
+ transaction.transaction.feePayer.body.fee = UInt64.from(
159
+ signedJson.zkappCommand.feePayer.body.fee
160
+ );
161
+ for (let i = 0; i < length; i++) {
162
+ const signature =
163
+ signedJson.zkappCommand.accountUpdates[i].authorization.signature;
164
+ if (signature !== undefined && signature !== null) {
165
+ transaction.transaction.accountUpdates[i].authorization.signature =
166
+ signedJson.zkappCommand.accountUpdates[i].authorization.signature;
167
+ }
168
+ }
169
+ return transaction;
170
+ }
171
+
172
+ export function serializeTransaction(
173
+ tx: Mina.Transaction<false, false> | Mina.Transaction<false, true>
174
+ ): string {
175
+ const length = tx.transaction.accountUpdates.length;
176
+ let i;
177
+ const blindingValues = [];
178
+ const forests: ForestSerialized[] = [];
179
+ for (i = 0; i < length; i++) {
180
+ const la = tx.transaction.accountUpdates[i].lazyAuthorization;
181
+ if (
182
+ la !== undefined &&
183
+ (la as any).blindingValue !== undefined &&
184
+ la.kind === "lazy-proof"
185
+ )
186
+ blindingValues.push(la.blindingValue.toJSON());
187
+ else blindingValues.push("");
188
+ const forest: ForestSerialized = { length: 0, items: [] };
189
+ serializeLazyAuthorization(
190
+ (tx.transaction.accountUpdates[i].lazyAuthorization as any)?.args,
191
+ forest
192
+ );
193
+ forests.push(forest);
194
+ }
195
+ const serializedTransaction = JSON.stringify(
196
+ {
197
+ tx: tx.toJSON(),
198
+ blindingValues,
199
+ forestJSONs: forests.map((f) => JSON.stringify(f)),
200
+ length,
201
+ fee: tx.transaction.feePayer.body.fee.toJSON(),
202
+ sender: tx.transaction.feePayer.body.publicKey.toBase58(),
203
+ nonce: tx.transaction.feePayer.body.nonce.toBigint().toString(),
204
+ },
205
+ null,
206
+ 2
207
+ );
208
+ return serializedTransaction;
209
+ }
210
+
211
+ function serializeLazyAuthorization(
212
+ lazyAuthorization: any,
213
+ serialized: ForestSerialized
214
+ ): void {
215
+ if (lazyAuthorization?.hash !== undefined && lazyAuthorization.hash.toJSON) {
216
+ serialized.items.push({
217
+ h: fieldToBase64(lazyAuthorization.hash),
218
+ });
219
+ }
220
+
221
+ if (
222
+ lazyAuthorization?.previousHash !== undefined &&
223
+ lazyAuthorization.previousHash.toJSON
224
+ ) {
225
+ serialized.items.push({
226
+ p: fieldToBase64(lazyAuthorization.previousHash),
227
+ });
228
+ }
229
+ if (
230
+ lazyAuthorization?.callData !== undefined &&
231
+ lazyAuthorization.callData.toJSON
232
+ ) {
233
+ serialized.items.push({
234
+ c: fieldToBase64(lazyAuthorization.callData),
235
+ });
236
+ }
237
+
238
+ if (lazyAuthorization?.id !== undefined) {
239
+ serialized.items.push({
240
+ i: lazyAuthorization.id,
241
+ });
242
+ }
243
+
244
+ if (Array.isArray(lazyAuthorization)) {
245
+ for (const item of lazyAuthorization) {
246
+ serializeLazyAuthorization(item, serialized);
247
+ }
248
+ }
249
+ if (typeof lazyAuthorization === "object") {
250
+ for (const key in lazyAuthorization) {
251
+ serializeLazyAuthorization(lazyAuthorization[key], serialized);
252
+ }
253
+ }
254
+ serialized.length = serialized.items.length;
255
+ }
256
+
257
+ function deserializeLazyAuthorization(
258
+ lazyAuthorization: any,
259
+ serialized: ForestSerialized
260
+ ): void {
261
+ if (serialized.restoredItems === undefined) serialized.restoredItems = 0;
262
+ if (lazyAuthorization?.hash !== undefined && lazyAuthorization.hash.toJSON) {
263
+ if (serialized.restoredItems >= serialized.length)
264
+ throw new Error("Restored more items than expected");
265
+ const hash = serialized.items[serialized.restoredItems].h;
266
+ if (hash === undefined)
267
+ throw new Error(`Hash is undefined for item ${serialized.restoredItems}`);
268
+ lazyAuthorization.hash = fieldFromBase64(hash);
269
+
270
+ serialized.restoredItems++;
271
+ }
272
+ if (
273
+ lazyAuthorization?.previousHash !== undefined &&
274
+ lazyAuthorization.previousHash.toJSON
275
+ ) {
276
+ if (serialized.restoredItems >= serialized.length)
277
+ throw new Error("Restored more items than expected");
278
+ const previousHash = serialized.items[serialized.restoredItems].p;
279
+ if (previousHash === undefined)
280
+ throw new Error(
281
+ `Previous hash is undefined for item ${serialized.restoredItems}`
282
+ );
283
+ lazyAuthorization.previousHash = fieldFromBase64(previousHash);
284
+ serialized.restoredItems++;
285
+ }
286
+ if (
287
+ lazyAuthorization?.callData !== undefined &&
288
+ lazyAuthorization.callData.toJSON
289
+ ) {
290
+ if (serialized.restoredItems >= serialized.length)
291
+ throw new Error("Restored more items than expected");
292
+ const callData = serialized.items[serialized.restoredItems].c;
293
+ if (callData === undefined)
294
+ throw new Error(
295
+ `Call data is undefined for item ${serialized.restoredItems}`
296
+ );
297
+ lazyAuthorization.callData = fieldFromBase64(callData);
298
+ serialized.restoredItems++;
299
+ }
300
+ if (lazyAuthorization?.id !== undefined) {
301
+ if (serialized.restoredItems >= serialized.length)
302
+ throw new Error("Restored more items than expected");
303
+ const id = serialized.items[serialized.restoredItems].i;
304
+ if (id === undefined)
305
+ throw new Error(`Id is undefined for item ${serialized.restoredItems}`);
306
+ lazyAuthorization.id = id;
307
+ serialized.restoredItems++;
308
+ }
309
+ if (Array.isArray(lazyAuthorization)) {
310
+ for (const item of lazyAuthorization) {
311
+ deserializeLazyAuthorization(item, serialized);
312
+ }
313
+ }
314
+ if (typeof lazyAuthorization === "object") {
315
+ for (const key in lazyAuthorization) {
316
+ deserializeLazyAuthorization(lazyAuthorization[key], serialized);
317
+ }
318
+ }
319
+ }
@@ -0,0 +1,26 @@
1
+ import { BlockBerryChain } from "./chain.js";
2
+ import { getZkAppTxFromBlockBerry } from "./blockberry.js";
3
+ const TIMEOUT = 1000 * 60 * 21;
4
+
5
+ export async function txStatusBlockberry(params: {
6
+ hash: string;
7
+ time: number;
8
+ chain: BlockBerryChain;
9
+ blockBerryApiKey: string;
10
+ timeout?: number;
11
+ }): Promise<string> {
12
+ const { hash, chain, time, blockBerryApiKey } = params;
13
+
14
+ const tx = await getZkAppTxFromBlockBerry({ hash, chain, blockBerryApiKey });
15
+ if (tx?.txStatus) return tx?.txStatus;
16
+ if (Date.now() - time > (params.timeout ?? TIMEOUT)) {
17
+ console.error(
18
+ "txStatus: Timeout while checking tx with blockberry",
19
+ chain,
20
+ hash
21
+ );
22
+ return "replaced";
23
+ } else {
24
+ return "pending";
25
+ }
26
+ }
@@ -0,0 +1,34 @@
1
+ import { Field } from "o1js";
2
+ import { toBase, fromBase } from "./base64.js";
3
+
4
+ // URL friendly base64 encoding
5
+ const TABLE =
6
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
7
+
8
+ export function fieldToBase56(field: Field): string {
9
+ const digits = toBase(field.toBigInt(), 56n);
10
+ //console.log("digits:", digits);
11
+ const str = digits.map((x) => TABLE[Number(x)]).join("");
12
+ //console.log("str:", str);
13
+ return str;
14
+ }
15
+
16
+ export function fieldFromBase56(str: string): Field {
17
+ const base56Digits = str.split("").map((x) => BigInt(TABLE.indexOf(x)));
18
+ const x = fromBase(base56Digits, 56n);
19
+ return Field(x);
20
+ }
21
+
22
+ export function fieldToBase64(field: Field): string {
23
+ const digits = toBase(field.toBigInt(), 64n);
24
+ //console.log("digits:", digits);
25
+ const str = digits.map((x) => TABLE[Number(x)]).join("");
26
+ //console.log("str:", str);
27
+ return str;
28
+ }
29
+
30
+ export function fieldFromBase64(str: string): Field {
31
+ const base64Digits = str.split("").map((x) => BigInt(TABLE.indexOf(x)));
32
+ const x = fromBase(base64Digits, 64n);
33
+ return Field(x);
34
+ }
@@ -0,0 +1,87 @@
1
+ // URL friendly base64 encoding
2
+ const TABLE =
3
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
4
+
5
+ export function bigintToBase56(value: bigint): string {
6
+ const digits = toBase(value, 56n);
7
+ //console.log("digits:", digits);
8
+ const str = digits.map((x) => TABLE[Number(x)]).join("");
9
+ //console.log("str:", str);
10
+ return str;
11
+ }
12
+
13
+ export function bigintFromBase56(str: string): bigint {
14
+ const base56Digits = str.split("").map((x) => BigInt(TABLE.indexOf(x)));
15
+ const x = fromBase(base56Digits, 56n);
16
+ return x;
17
+ }
18
+
19
+ export function bigintToBase64(value: bigint): string {
20
+ const digits = toBase(value, 64n);
21
+ //console.log("digits:", digits);
22
+ const str = digits.map((x) => TABLE[Number(x)]).join("");
23
+ //console.log("str:", str);
24
+ return str;
25
+ }
26
+
27
+ export function bigintFromBase64(str: string): bigint {
28
+ const base64Digits = str.split("").map((x) => BigInt(TABLE.indexOf(x)));
29
+ const x = fromBase(base64Digits, 64n);
30
+ return x;
31
+ }
32
+
33
+ export function fromBase(digits: bigint[], base: bigint) {
34
+ if (base <= 0n) throw Error("fromBase: base must be positive");
35
+ // compute powers base, base^2, base^4, ..., base^(2^k)
36
+ // with largest k s.t. n = 2^k < digits.length
37
+ let basePowers = [];
38
+ for (let power = base, n = 1; n < digits.length; power **= 2n, n *= 2) {
39
+ basePowers.push(power);
40
+ }
41
+ let k = basePowers.length;
42
+ // pad digits array with zeros s.t. digits.length === 2^k
43
+ digits = digits.concat(Array(2 ** k - digits.length).fill(0n));
44
+ // accumulate [x0, x1, x2, x3, ...] -> [x0 + base*x1, x2 + base*x3, ...] -> [x0 + base*x1 + base^2*(x2 + base*x3), ...] -> ...
45
+ // until we end up with a single element
46
+ for (let i = 0; i < k; i++) {
47
+ let newDigits = Array(digits.length >> 1);
48
+ let basePower = basePowers[i];
49
+ for (let j = 0; j < newDigits.length; j++) {
50
+ newDigits[j] = digits[2 * j] + basePower * digits[2 * j + 1];
51
+ }
52
+ digits = newDigits;
53
+ }
54
+ console.assert(digits.length === 1);
55
+ let [digit] = digits;
56
+ return digit;
57
+ }
58
+
59
+ export function toBase(x: bigint, base: bigint) {
60
+ if (base <= 0n) throw Error("toBase: base must be positive");
61
+ // compute powers base, base^2, base^4, ..., base^(2^k)
62
+ // with largest k s.t. base^(2^k) < x
63
+ let basePowers = [];
64
+ for (let power = base; power <= x; power **= 2n) {
65
+ basePowers.push(power);
66
+ }
67
+ let digits = [x]; // single digit w.r.t base^(2^(k+1))
68
+ // successively split digits w.r.t. base^(2^j) into digits w.r.t. base^(2^(j-1))
69
+ // until we arrive at digits w.r.t. base
70
+ let k = basePowers.length;
71
+ for (let i = 0; i < k; i++) {
72
+ let newDigits = Array(2 * digits.length);
73
+ let basePower = basePowers[k - 1 - i];
74
+ for (let j = 0; j < digits.length; j++) {
75
+ let x = digits[j];
76
+ let high = x / basePower;
77
+ newDigits[2 * j + 1] = high;
78
+ newDigits[2 * j] = x - high * basePower;
79
+ }
80
+ digits = newDigits;
81
+ }
82
+ // pop "leading" zero digits
83
+ while (digits[digits.length - 1] === 0n) {
84
+ digits.pop();
85
+ }
86
+ return digits;
87
+ }