@ton-community/ton-ledger 5.0.0 → 7.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 +12 -0
- package/README.md +24 -10
- package/dist/TonTransport.d.ts +1 -4
- package/dist/TonTransport.js +17 -20
- package/dist/utils/getInit.d.ts +1 -1
- package/dist/utils/getInit.js +3 -3
- package/dist/utils/ledgerWriter.d.ts +1 -1
- package/dist/utils/ledgerWriter.js +3 -3
- package/dist/utils/ledgerWriter.spec.js +9 -9
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
5
5
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [0.7.0] - 2023-09-15
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- Switched `ton-core` and `ton-crypto` to `@ton/core` and `@ton/crypto`
|
|
12
|
+
|
|
13
|
+
## [6.0.0] - 2023-07-11
|
|
14
|
+
|
|
15
|
+
### Removed
|
|
16
|
+
|
|
17
|
+
- Removed `unsafe` payload format
|
|
18
|
+
|
|
7
19
|
## [5.0.0] - 2023-06-29
|
|
8
20
|
|
|
9
21
|
### Removed
|
package/README.md
CHANGED
|
@@ -118,28 +118,42 @@ await c.sendExternalMessage(contract, signed);
|
|
|
118
118
|
|
|
119
119
|
## Payload formats
|
|
120
120
|
|
|
121
|
-
Usually you want to perform transactions with some payload. Ledger's NanoApp currently supports 2 stable commands, all other are outdated or unstable:
|
|
122
|
-
|
|
123
121
|
### Transaction with a comment
|
|
124
122
|
Comments are limited to ASCII-only symbols and 127 letters. Anything above would be automatically downgraded to Blind Signing Mode that you want to avoid at all cost.
|
|
125
123
|
|
|
126
124
|
```typescript
|
|
127
|
-
|
|
125
|
+
const payload: TonPayloadFormat = {
|
|
128
126
|
type: 'comment',
|
|
129
127
|
text: 'Deposit'
|
|
130
128
|
};
|
|
131
129
|
```
|
|
132
130
|
|
|
133
|
-
###
|
|
131
|
+
### Jetton transfer
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
const payload: TonPayloadFormat = {
|
|
135
|
+
type: 'jetton-transfer',
|
|
136
|
+
queryId: null, // null will be replaced with 0; you can pass any value of the BigInt type
|
|
137
|
+
amount: 1n,
|
|
138
|
+
destination: Address.parse('EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c'),
|
|
139
|
+
responseDestination: Address.parse('EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c'),
|
|
140
|
+
customPayload: null, // you can pass any value of the Cell type
|
|
141
|
+
forwardAmount: 0n,
|
|
142
|
+
forwardPayload: null // you can pass any value of the Cell type
|
|
143
|
+
};
|
|
144
|
+
```
|
|
134
145
|
|
|
135
|
-
|
|
146
|
+
### NFT transfer
|
|
136
147
|
|
|
137
148
|
```typescript
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
149
|
+
const payload: TonPayloadFormat = {
|
|
150
|
+
type: 'nft-transfer',
|
|
151
|
+
queryId: null, // null will be replaced with 0; you can pass any value of the BigInt type
|
|
152
|
+
newOwner: Address.parse('EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c'),
|
|
153
|
+
responseDestination: Address.parse('EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c'),
|
|
154
|
+
customPayload: null, // you can pass any value of the Cell type
|
|
155
|
+
forwardAmount: 0n,
|
|
156
|
+
forwardPayload: null // you can pass any value of the Cell type
|
|
143
157
|
};
|
|
144
158
|
```
|
|
145
159
|
|
package/dist/TonTransport.d.ts
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import Transport from "@ledgerhq/hw-transport";
|
|
3
|
-
import { Address, Cell, SendMode, StateInit } from "ton
|
|
3
|
+
import { Address, Cell, SendMode, StateInit } from "@ton/core";
|
|
4
4
|
export type TonPayloadFormat = {
|
|
5
|
-
type: 'unsafe';
|
|
6
|
-
message: Cell;
|
|
7
|
-
} | {
|
|
8
5
|
type: 'comment';
|
|
9
6
|
text: string;
|
|
10
7
|
} | {
|
package/dist/TonTransport.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.TonTransport = void 0;
|
|
4
|
-
const
|
|
5
|
-
const
|
|
4
|
+
const core_1 = require("@ton/core");
|
|
5
|
+
const crypto_1 = require("@ton/crypto");
|
|
6
6
|
const teslabot_1 = require("teslabot");
|
|
7
7
|
const ledgerWriter_1 = require("./utils/ledgerWriter");
|
|
8
8
|
const getInit_1 = require("./utils/getInit");
|
|
@@ -80,7 +80,7 @@ class TonTransport {
|
|
|
80
80
|
}
|
|
81
81
|
// Contract
|
|
82
82
|
const contract = (0, getInit_1.getInit)(chain, response);
|
|
83
|
-
const address = (0,
|
|
83
|
+
const address = (0, core_1.contractAddress)(chain, contract);
|
|
84
84
|
return { address: address.toString({ bounceable, testOnly }), publicKey: response };
|
|
85
85
|
}
|
|
86
86
|
async validateAddress(path, opts) {
|
|
@@ -95,7 +95,7 @@ class TonTransport {
|
|
|
95
95
|
}
|
|
96
96
|
// Contract
|
|
97
97
|
const contract = (0, getInit_1.getInit)(chain, response);
|
|
98
|
-
const address = (0,
|
|
98
|
+
const address = (0, core_1.contractAddress)(chain, contract);
|
|
99
99
|
return { address: address.toString({ bounceable, testOnly }), publicKey: response };
|
|
100
100
|
}
|
|
101
101
|
async getAddressProof(path, params, opts) {
|
|
@@ -116,7 +116,7 @@ class TonTransport {
|
|
|
116
116
|
let res = await this.#doRequest(INS_PROOF, 0x01, flags, reqBuf);
|
|
117
117
|
let signature = res.slice(1, 1 + 64);
|
|
118
118
|
let hash = res.slice(2 + 64, 2 + 64 + 32);
|
|
119
|
-
if (!(0,
|
|
119
|
+
if (!(0, crypto_1.signVerify)(hash, signature, publicKey)) {
|
|
120
120
|
throw Error('Received signature is invalid');
|
|
121
121
|
}
|
|
122
122
|
return { signature, hash };
|
|
@@ -132,7 +132,7 @@ class TonTransport {
|
|
|
132
132
|
case 'plaintext': {
|
|
133
133
|
schema = 0x754bf91b;
|
|
134
134
|
data = Buffer.from(req.text, 'ascii');
|
|
135
|
-
cell = (0,
|
|
135
|
+
cell = (0, core_1.beginCell)().storeStringTail(req.text).endCell();
|
|
136
136
|
break;
|
|
137
137
|
}
|
|
138
138
|
case 'app-data': {
|
|
@@ -140,7 +140,7 @@ class TonTransport {
|
|
|
140
140
|
throw new Error('At least one of `address` and `domain` must be set when using \'app-data\' request');
|
|
141
141
|
}
|
|
142
142
|
schema = 0x54b58535;
|
|
143
|
-
let b = (0,
|
|
143
|
+
let b = (0, core_1.beginCell)();
|
|
144
144
|
let dp = [];
|
|
145
145
|
if (req.address !== undefined) {
|
|
146
146
|
b.storeBit(1);
|
|
@@ -153,7 +153,7 @@ class TonTransport {
|
|
|
153
153
|
}
|
|
154
154
|
if (req.domain !== undefined) {
|
|
155
155
|
b.storeBit(1);
|
|
156
|
-
let inner = (0,
|
|
156
|
+
let inner = (0, core_1.beginCell)();
|
|
157
157
|
req.domain.split('.').reverse().forEach(p => {
|
|
158
158
|
inner.storeBuffer(Buffer.from(p, 'ascii'));
|
|
159
159
|
inner.storeUint(0, 8);
|
|
@@ -204,7 +204,7 @@ class TonTransport {
|
|
|
204
204
|
if (!hash.equals(cell.hash())) {
|
|
205
205
|
throw Error('Hash mismatch. Expected: ' + cell.hash().toString('hex') + ', got: ' + hash.toString('hex'));
|
|
206
206
|
}
|
|
207
|
-
if (!(0,
|
|
207
|
+
if (!(0, crypto_1.signVerify)(Buffer.concat([commonPart, hash]), signature, publicKey)) {
|
|
208
208
|
throw Error('Received signature is invalid');
|
|
209
209
|
}
|
|
210
210
|
return {
|
|
@@ -237,8 +237,8 @@ class TonTransport {
|
|
|
237
237
|
//
|
|
238
238
|
let stateInit = null;
|
|
239
239
|
if (transaction.stateInit) {
|
|
240
|
-
stateInit = (0,
|
|
241
|
-
.store((0,
|
|
240
|
+
stateInit = (0, core_1.beginCell)()
|
|
241
|
+
.store((0, core_1.storeStateInit)(transaction.stateInit))
|
|
242
242
|
.endCell();
|
|
243
243
|
pkg = Buffer.concat([
|
|
244
244
|
pkg,
|
|
@@ -266,20 +266,17 @@ class TonTransport {
|
|
|
266
266
|
(0, ledgerWriter_1.writeUint16)(Buffer.from(transaction.payload.text).length),
|
|
267
267
|
Buffer.from(transaction.payload.text)
|
|
268
268
|
]);
|
|
269
|
-
payload = (0,
|
|
269
|
+
payload = (0, core_1.beginCell)()
|
|
270
270
|
.storeUint(0, 32)
|
|
271
271
|
.storeBuffer(Buffer.from(transaction.payload.text))
|
|
272
272
|
.endCell();
|
|
273
273
|
}
|
|
274
|
-
else if (transaction.payload.type === 'unsafe') {
|
|
275
|
-
payload = transaction.payload.message;
|
|
276
|
-
}
|
|
277
274
|
else if (transaction.payload.type === 'jetton-transfer' || transaction.payload.type === 'nft-transfer') {
|
|
278
275
|
hints = Buffer.concat([
|
|
279
276
|
(0, ledgerWriter_1.writeUint8)(1),
|
|
280
277
|
(0, ledgerWriter_1.writeUint32)(transaction.payload.type === 'jetton-transfer' ? 0x01 : 0x02)
|
|
281
278
|
]);
|
|
282
|
-
let b = (0,
|
|
279
|
+
let b = (0, core_1.beginCell)()
|
|
283
280
|
.storeUint(transaction.payload.type === 'jetton-transfer' ? 0x0f8a7ea5 : 0x5fcc3d14, 32);
|
|
284
281
|
let d = Buffer.alloc(0);
|
|
285
282
|
if (transaction.payload.queryId !== null) {
|
|
@@ -359,7 +356,7 @@ class TonTransport {
|
|
|
359
356
|
//
|
|
360
357
|
// Parse response
|
|
361
358
|
//
|
|
362
|
-
let orderBuilder = (0,
|
|
359
|
+
let orderBuilder = (0, core_1.beginCell)()
|
|
363
360
|
.storeBit(0)
|
|
364
361
|
.storeBit(true)
|
|
365
362
|
.storeBit(transaction.bounce)
|
|
@@ -394,7 +391,7 @@ class TonTransport {
|
|
|
394
391
|
.storeBit(false);
|
|
395
392
|
}
|
|
396
393
|
// Transfer message
|
|
397
|
-
let transfer = (0,
|
|
394
|
+
let transfer = (0, core_1.beginCell)()
|
|
398
395
|
.storeUint(698983191, 32)
|
|
399
396
|
.storeUint(transaction.timeout, 32)
|
|
400
397
|
.storeUint(transaction.seqno, 32)
|
|
@@ -408,11 +405,11 @@ class TonTransport {
|
|
|
408
405
|
if (!hash.equals(transfer.hash())) {
|
|
409
406
|
throw Error('Hash mismatch. Expected: ' + transfer.hash().toString('hex') + ', got: ' + hash.toString('hex'));
|
|
410
407
|
}
|
|
411
|
-
if (!(0,
|
|
408
|
+
if (!(0, crypto_1.signVerify)(hash, signature, publicKey)) {
|
|
412
409
|
throw Error('Received signature is invalid');
|
|
413
410
|
}
|
|
414
411
|
// Build a message
|
|
415
|
-
return (0,
|
|
412
|
+
return (0, core_1.beginCell)()
|
|
416
413
|
.storeBuffer(signature)
|
|
417
414
|
.storeSlice(transfer.beginParse())
|
|
418
415
|
.endCell();
|
package/dist/utils/getInit.d.ts
CHANGED
package/dist/utils/getInit.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getInit = void 0;
|
|
4
|
-
const
|
|
4
|
+
const core_1 = require("@ton/core");
|
|
5
5
|
function getInit(workchain, publicKey) {
|
|
6
|
-
let code =
|
|
7
|
-
let data = (0,
|
|
6
|
+
let code = core_1.Cell.fromBoc(Buffer.from('te6ccgECFAEAAtQAART/APSkE/S88sgLAQIBIAIDAgFIBAUE+PKDCNcYINMf0x/THwL4I7vyZO1E0NMf0x/T//QE0VFDuvKhUVG68qIF+QFUEGT5EPKj+AAkpMjLH1JAyx9SMMv/UhD0AMntVPgPAdMHIcAAn2xRkyDXSpbTB9QC+wDoMOAhwAHjACHAAuMAAcADkTDjDQOkyMsfEssfy/8QERITAubQAdDTAyFxsJJfBOAi10nBIJJfBOAC0x8hghBwbHVnvSKCEGRzdHK9sJJfBeAD+kAwIPpEAcjKB8v/ydDtRNCBAUDXIfQEMFyBAQj0Cm+hMbOSXwfgBdM/yCWCEHBsdWe6kjgw4w0DghBkc3RyupJfBuMNBgcCASAICQB4AfoA9AQw+CdvIjBQCqEhvvLgUIIQcGx1Z4MesXCAGFAEywUmzxZY+gIZ9ADLaRfLH1Jgyz8gyYBA+wAGAIpQBIEBCPRZMO1E0IEBQNcgyAHPFvQAye1UAXKwjiOCEGRzdHKDHrFwgBhQBcsFUAPPFiP6AhPLassfyz/JgED7AJJfA+ICASAKCwBZvSQrb2omhAgKBrkPoCGEcNQICEekk30pkQzmkD6f+YN4EoAbeBAUiYcVnzGEAgFYDA0AEbjJftRNDXCx+AA9sp37UTQgQFA1yH0BDACyMoHy//J0AGBAQj0Cm+hMYAIBIA4PABmtznaiaEAga5Drhf/AABmvHfaiaEAQa5DrhY/AAG7SB/oA1NQi+QAFyMoHFcv/ydB3dIAYyMsFywIizxZQBfoCFMtrEszMyXP7AMhAFIEBCPRR8qcCAHCBAQjXGPoA0z/IVCBHgQEI9FHyp4IQbm90ZXB0gBjIywXLAlAGzxZQBPoCFMtqEssfyz/Jc/sAAgBsgQEI1xj6ANM/MFIkgQEI9Fnyp4IQZHN0cnB0gBjIywXLAlAFzxZQA/oCE8tqyx8Syz/Jc/sAAAr0AMntVA==', 'base64'))[0];
|
|
7
|
+
let data = (0, core_1.beginCell)()
|
|
8
8
|
.storeUint(0, 32) // Seqno
|
|
9
9
|
.storeUint(698983191 + workchain, 32)
|
|
10
10
|
.storeBuffer(publicKey)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
-
import { Address, Cell } from 'ton
|
|
2
|
+
import { Address, Cell } from '@ton/core';
|
|
3
3
|
export declare function writeUint32(value: number): Buffer;
|
|
4
4
|
export declare function writeUint16(value: number): Buffer;
|
|
5
5
|
export declare function writeUint64(value: bigint): Buffer;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.writeCellRef = exports.writeAddress = exports.writeUint8 = exports.writeVarUInt = exports.writeUint64 = exports.writeUint16 = exports.writeUint32 = void 0;
|
|
4
|
-
const
|
|
4
|
+
const core_1 = require("@ton/core");
|
|
5
5
|
function writeUint32(value) {
|
|
6
6
|
let b = Buffer.alloc(4);
|
|
7
7
|
b.writeUint32BE(value, 0);
|
|
@@ -15,12 +15,12 @@ function writeUint16(value) {
|
|
|
15
15
|
}
|
|
16
16
|
exports.writeUint16 = writeUint16;
|
|
17
17
|
function writeUint64(value) {
|
|
18
|
-
return (0,
|
|
18
|
+
return (0, core_1.beginCell)().storeUint(value, 64).endCell().beginParse().loadBuffer(8);
|
|
19
19
|
}
|
|
20
20
|
exports.writeUint64 = writeUint64;
|
|
21
21
|
function writeVarUInt(value) {
|
|
22
22
|
const sizeBytes = Math.ceil((value.toString(2).length) / 8);
|
|
23
|
-
return (0,
|
|
23
|
+
return (0, core_1.beginCell)().storeUint(sizeBytes, 8).storeUint(value, sizeBytes * 8).endCell().beginParse().loadBuffer(1 + sizeBytes);
|
|
24
24
|
}
|
|
25
25
|
exports.writeVarUInt = writeVarUInt;
|
|
26
26
|
function writeUint8(value) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const
|
|
3
|
+
const core_1 = require("@ton/core");
|
|
4
4
|
const ledgerWriter_1 = require("./ledgerWriter");
|
|
5
5
|
describe('ledgerWriter', () => {
|
|
6
6
|
it('should write ints', () => {
|
|
@@ -27,15 +27,15 @@ describe('ledgerWriter', () => {
|
|
|
27
27
|
expect((0, ledgerWriter_1.writeUint64)(18446744073709551615n).toString('hex')).toMatchSnapshot();
|
|
28
28
|
});
|
|
29
29
|
it('should write addresses', () => {
|
|
30
|
-
expect((0, ledgerWriter_1.writeAddress)(new
|
|
31
|
-
expect((0, ledgerWriter_1.writeAddress)(new
|
|
32
|
-
expect((0, ledgerWriter_1.writeAddress)(
|
|
33
|
-
expect((0, ledgerWriter_1.writeAddress)(
|
|
30
|
+
expect((0, ledgerWriter_1.writeAddress)(new core_1.Address(0, Buffer.alloc(32))).toString('hex')).toMatchSnapshot();
|
|
31
|
+
expect((0, ledgerWriter_1.writeAddress)(new core_1.Address(-1, Buffer.alloc(32))).toString('hex')).toMatchSnapshot();
|
|
32
|
+
expect((0, ledgerWriter_1.writeAddress)(core_1.Address.parse('EQBNVUFfKt2QgqKL5vZvnyP50wmniCFP2ASOKAE-g2noRDlR')).toString('hex')).toMatchSnapshot();
|
|
33
|
+
expect((0, ledgerWriter_1.writeAddress)(core_1.Address.parse('Ef87m7_QrVM4uXAPCDM4DuF9Rj5Rwa5nHubwiQG96JmyAjQY')).toString('hex')).toMatchSnapshot();
|
|
34
34
|
});
|
|
35
35
|
it('should write cell refs', () => {
|
|
36
|
-
expect((0, ledgerWriter_1.writeCellRef)((0,
|
|
37
|
-
expect((0, ledgerWriter_1.writeCellRef)((0,
|
|
38
|
-
expect((0, ledgerWriter_1.writeCellRef)((0,
|
|
39
|
-
expect((0, ledgerWriter_1.writeCellRef)((0,
|
|
36
|
+
expect((0, ledgerWriter_1.writeCellRef)((0, core_1.beginCell)().endCell()).toString('hex')).toMatchSnapshot();
|
|
37
|
+
expect((0, ledgerWriter_1.writeCellRef)((0, core_1.beginCell)().storeUint(0, 32).endCell()).toString('hex')).toMatchSnapshot();
|
|
38
|
+
expect((0, ledgerWriter_1.writeCellRef)((0, core_1.beginCell)().storeUint(0, 32).storeRef((0, core_1.beginCell)().endCell()).endCell()).toString('hex')).toMatchSnapshot();
|
|
39
|
+
expect((0, ledgerWriter_1.writeCellRef)((0, core_1.beginCell)().storeUint(0, 32).storeRef((0, core_1.beginCell)().storeRef((0, core_1.beginCell)().endCell()).endCell()).endCell()).toString('hex')).toMatchSnapshot();
|
|
40
40
|
});
|
|
41
41
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ton-community/ton-ledger",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.0",
|
|
4
4
|
"repository": "https://github.com/ton-community/ton-ledger-ts",
|
|
5
5
|
"author": "Steve Korshakov <steve@korshakov.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -15,24 +15,24 @@
|
|
|
15
15
|
"dev": "ts-node ./test/index.ts"
|
|
16
16
|
},
|
|
17
17
|
"peerDependencies": {
|
|
18
|
-
"ton
|
|
18
|
+
"@ton/core": ">=0.52.2"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
|
-
"@release-it/keep-a-changelog": "^3.1.0",
|
|
22
21
|
"@ledgerhq/hw-transport-node-hid": "^6.27.15",
|
|
22
|
+
"@release-it/keep-a-changelog": "^3.1.0",
|
|
23
|
+
"@ton/core": "^0.52.2",
|
|
23
24
|
"@types/jest": "^29.5.2",
|
|
24
25
|
"@types/node": "^20.2.5",
|
|
25
26
|
"jest": "^29.5.0",
|
|
26
27
|
"release-it": "^15.11.0",
|
|
27
|
-
"ton-core": "^0.49.1",
|
|
28
28
|
"ts-jest": "^29.1.0",
|
|
29
29
|
"ts-node": "^10.9.1",
|
|
30
30
|
"typescript": "^4.9.5"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@ledgerhq/hw-transport": "^6.28.4",
|
|
34
|
-
"
|
|
35
|
-
"
|
|
34
|
+
"@ton/crypto": "^3.2.0",
|
|
35
|
+
"teslabot": "^1.5.0"
|
|
36
36
|
},
|
|
37
37
|
"publishConfig": {
|
|
38
38
|
"access": "public",
|