@reality.eth/reality-eth-lib 3.4.25 → 3.4.29
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/README.md +34 -0
- package/browser-entry.ts +34 -0
- package/build-browser.mjs +24 -0
- package/dist/cjs/formatters/question.d.ts +58 -0
- package/dist/cjs/formatters/question.js +537 -0
- package/dist/cjs/formatters/template.d.ts +4 -0
- package/dist/cjs/formatters/template.js +24 -0
- package/formatters/question.js +3 -601
- package/formatters/template.js +2 -27
- package/package.json +25 -11
- package/src/formatters/question.ts +574 -0
- package/src/formatters/template.ts +19 -0
- package/test/formatters.js +49 -1
- package/test-ponder.mjs +265 -0
- package/tsconfig.json +17 -0
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function defaultTemplateIDForType(template_type: string): number;
|
|
2
|
+
export declare function defaultTemplateForType(template_type: string): string;
|
|
3
|
+
export declare function preloadedTemplateContents(): Record<string, string>;
|
|
4
|
+
export declare function preloadedTemplateContentsV32(): Record<string, string>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.defaultTemplateIDForType = defaultTemplateIDForType;
|
|
7
|
+
exports.defaultTemplateForType = defaultTemplateForType;
|
|
8
|
+
exports.preloadedTemplateContents = preloadedTemplateContents;
|
|
9
|
+
exports.preloadedTemplateContentsV32 = preloadedTemplateContentsV32;
|
|
10
|
+
const templates_json_1 = __importDefault(require("@reality.eth/contracts/config/templates.json"));
|
|
11
|
+
const templates_3_2_json_1 = __importDefault(require("@reality.eth/contracts/config/templates_3.2.json"));
|
|
12
|
+
function defaultTemplateIDForType(template_type) {
|
|
13
|
+
return templates_json_1.default.base_ids[template_type];
|
|
14
|
+
}
|
|
15
|
+
function defaultTemplateForType(template_type) {
|
|
16
|
+
return templates_json_1.default.content[String(defaultTemplateIDForType(template_type))];
|
|
17
|
+
}
|
|
18
|
+
function preloadedTemplateContents() {
|
|
19
|
+
return templates_json_1.default.content;
|
|
20
|
+
}
|
|
21
|
+
// v3.2+ contracts (and v2.2) use description instead of category and add template 5 (hash type).
|
|
22
|
+
function preloadedTemplateContentsV32() {
|
|
23
|
+
return templates_3_2_json_1.default.content;
|
|
24
|
+
}
|
package/formatters/question.js
CHANGED
|
@@ -1,601 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const BigNumber = require('bignumber.js');
|
|
5
|
-
const ethereumjs_abi = require('ethereumjs-abi')
|
|
6
|
-
const vsprintf = require("sprintf-js").vsprintf
|
|
7
|
-
const QUESTION_MAX_OUTCOMES = 128;
|
|
8
|
-
const TEMPLATE_MAX_PLACEHOLDERS = 128;
|
|
9
|
-
const marked = require('marked');
|
|
10
|
-
const DOMPurify = require('isomorphic-dompurify');
|
|
11
|
-
const { convert} = require('html-to-text');
|
|
12
|
-
marked.setOptions({headerIds: false});
|
|
13
|
-
|
|
14
|
-
exports.delimiter = function() {
|
|
15
|
-
return '\u241f'; // Thought about '\u0000' but it seems to break something;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
exports.contentHash = function(template_id, opening_ts, content) {
|
|
19
|
-
return "0x" + ethereumjs_abi.soliditySHA3(
|
|
20
|
-
["uint256", "uint32", "string"],
|
|
21
|
-
[ new BN(template_id), new BN(opening_ts), content]
|
|
22
|
-
).toString('hex');
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
exports.questionID = function(template_id, question, arbitrator, timeout, opening_ts, sender, nonce, min_bond, contract, version) {
|
|
26
|
-
// If there's a min_bond or a contract, insist we also get a version parameter
|
|
27
|
-
if (typeof version === 'undefined') {
|
|
28
|
-
if (typeof min_bond === 'string' || typeof contract === 'string') {
|
|
29
|
-
throw Error("Version not defined");
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
if (!version) {
|
|
33
|
-
version = '2.0';
|
|
34
|
-
}
|
|
35
|
-
const vernum = parseInt(version);
|
|
36
|
-
if (isNaN(vernum) || vernum <2 || vernum > 4) {
|
|
37
|
-
throw Error("Version not recognized");
|
|
38
|
-
}
|
|
39
|
-
if (vernum >= 3) {
|
|
40
|
-
if (typeof min_bond !== 'string') {
|
|
41
|
-
throw Error('min_bond not supplied or invalid. Required in v3. Pass "0x0" for a zero bond')
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
var content_hash = module.exports.contentHash(template_id, opening_ts, question);
|
|
46
|
-
|
|
47
|
-
let qid;
|
|
48
|
-
if (vernum < 3) {
|
|
49
|
-
qid = ethereumjs_abi.soliditySHA3(
|
|
50
|
-
//["bytes32", "address", "uint256", "address", "uint256"],
|
|
51
|
-
["uint256", "address", "uint32", "address", "uint256"],
|
|
52
|
-
[ new BN(content_hash.replace(/^0x/, ''), 16), arbitrator, new BN(timeout), sender, new BN(nonce)]
|
|
53
|
-
);
|
|
54
|
-
} else {
|
|
55
|
-
// bytes32 question_id = keccak256(abi.encodePacked(content_hash, arbitrator, timeout, uint256(min_bond), address(this), msg.sender, nonce));
|
|
56
|
-
qid = ethereumjs_abi.soliditySHA3(
|
|
57
|
-
["uint256", "address", "uint32", "uint256", "address", "address", "uint256"],
|
|
58
|
-
[ new BN(content_hash.replace(/^0x/, ''), 16), arbitrator, new BN(timeout), new BN(min_bond.replace(/^0x/, ''), 16), contract, sender, new BN(nonce)]
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// The seems to be something wrong with how soliditySHA3 handles bytes32, so tell it we gave it uint256
|
|
63
|
-
return "0x" + qid.toString('hex');
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
exports.minNumber = function(qjson) {
|
|
67
|
-
var is_signed = (qjson['type'] == 'int');
|
|
68
|
-
if (!is_signed) {
|
|
69
|
-
return new BigNumber(0);
|
|
70
|
-
}
|
|
71
|
-
return module.exports.maxNumber(qjson).neg();
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
exports.maxNumber = function(qjson) {
|
|
75
|
-
var is_signed = (qjson['type'] == 'int');
|
|
76
|
-
var divby = new BigNumber(1);
|
|
77
|
-
if (qjson['decimals']) {
|
|
78
|
-
divby = new BigNumber(10).pow(new BigNumber(qjson['decimals']));
|
|
79
|
-
}
|
|
80
|
-
if (is_signed) {
|
|
81
|
-
divby = divby.times(2);
|
|
82
|
-
}
|
|
83
|
-
return new BigNumber("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").dividedBy(divby);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
exports.arrayToBitmaskBigNumber = function(selections) {
|
|
87
|
-
// console.log('selections are ', selections);
|
|
88
|
-
var bitstr = '';
|
|
89
|
-
for (var i=0; i<selections.length; i++) {
|
|
90
|
-
var item = selections[i] ? '1' : '0';
|
|
91
|
-
bitstr = item + bitstr;
|
|
92
|
-
}
|
|
93
|
-
//console.log('bitstr',bitstr);
|
|
94
|
-
return new BigNumber(bitstr, 2);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
exports.answerToBytes32 = function(answer, qjson) {
|
|
98
|
-
var qtype = qjson['type'];
|
|
99
|
-
if (qtype == 'hash') {
|
|
100
|
-
return module.exports.bytes32ToString(answer, qjson);
|
|
101
|
-
}
|
|
102
|
-
if (qtype == 'multiple-select') {
|
|
103
|
-
answer = module.exports.arrayToBitmaskBigNumber(answer);
|
|
104
|
-
}
|
|
105
|
-
var decimals = (qtype == 'uint') ? parseInt(qjson['decimals']) : 0;
|
|
106
|
-
if (!decimals) {
|
|
107
|
-
decimals = 0;
|
|
108
|
-
}
|
|
109
|
-
if (decimals > 0) {
|
|
110
|
-
var multiplier = new BigNumber(10).pow(new BigNumber(decimals));
|
|
111
|
-
answer = new BigNumber(answer).times(multiplier).toString(16);
|
|
112
|
-
} else {
|
|
113
|
-
answer = new BigNumber(answer).toString(16);
|
|
114
|
-
}
|
|
115
|
-
//console.log('muliplied to ',answer.toString());
|
|
116
|
-
var bn;
|
|
117
|
-
if (qtype == 'int') {
|
|
118
|
-
bn = new BN(answer, 16).toTwos(256);
|
|
119
|
-
} else if (qtype == 'uint') {
|
|
120
|
-
bn = new BN(answer, 16);
|
|
121
|
-
} else {
|
|
122
|
-
return module.exports.padToBytes32(new BigNumber(answer).toString(16));
|
|
123
|
-
}
|
|
124
|
-
return module.exports.padToBytes32(bn.toString(16));
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
exports.bytes32ToString = function(bytes32str, qjson) {
|
|
128
|
-
var qtype = qjson['type'];
|
|
129
|
-
var decimals = parseInt(qjson['decimals']);
|
|
130
|
-
if (!decimals) {
|
|
131
|
-
decimals = 0;
|
|
132
|
-
}
|
|
133
|
-
bytes32str = bytes32str.replace(/^0x/, '');
|
|
134
|
-
var bn;
|
|
135
|
-
if (qtype == 'int') {
|
|
136
|
-
var bn = new BN(bytes32str, 16).fromTwos(256);
|
|
137
|
-
} else if (qtype == 'uint' || qtype == 'datetime') {
|
|
138
|
-
var bn = new BN(bytes32str, 16);
|
|
139
|
-
} else if (qtype == 'hash') {
|
|
140
|
-
var bn = new BN(bytes32str, 16);
|
|
141
|
-
return module.exports.padToBytes32(bn.toString('hex')).toLowerCase();
|
|
142
|
-
} else {
|
|
143
|
-
throw Error("Unrecognized answer type " + qtype);
|
|
144
|
-
}
|
|
145
|
-
var ans = bn.toString();
|
|
146
|
-
// Do the decimals with BigNumber as it seems to work better
|
|
147
|
-
if (decimals > 0) {
|
|
148
|
-
var multiplier = new BigNumber(10).pow(new BigNumber(decimals));
|
|
149
|
-
ans = new BigNumber(ans).dividedBy(multiplier).toString();
|
|
150
|
-
}
|
|
151
|
-
return ans.toString();
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
exports.padToBytes32 = function(n, raw) {
|
|
155
|
-
while (n.length < 64) {
|
|
156
|
-
n = "0" + n;
|
|
157
|
-
}
|
|
158
|
-
if (raw) {
|
|
159
|
-
return n;
|
|
160
|
-
}
|
|
161
|
-
return "0x" + n;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
exports.convertTsToString = function(ts) {
|
|
165
|
-
if (typeof ts.toNumber === 'function') {
|
|
166
|
-
ts = ts.toNumber();
|
|
167
|
-
}
|
|
168
|
-
let date = new Date();
|
|
169
|
-
date.setTime(ts * 1000);
|
|
170
|
-
return date.toISOString();
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
exports.secondsTodHms = function(sec) {
|
|
174
|
-
sec = Number(sec);
|
|
175
|
-
let d = Math.floor(sec / (3600 * 24));
|
|
176
|
-
let h = Math.floor(sec % (3600 * 24) / 3600);
|
|
177
|
-
let m = Math.floor(sec % (3600 * 24) % 3600 / 60);
|
|
178
|
-
let s = Math.floor(sec % (3600 * 24) % 3600 % 60);
|
|
179
|
-
|
|
180
|
-
let dDisplay = d > 0 ? d + (d == 1 ? " day " : " days ") : "";
|
|
181
|
-
let hDisplay = h > 0 ? h + (h == 1 ? " hour " : " hours ") : "";
|
|
182
|
-
let mDisplay = m > 0 ? m + (m == 1 ? " minute " : " minutes ") : "";
|
|
183
|
-
let sDisplay = s > 0 ? s + (s == 1 ? " second" : " seconds") : "";
|
|
184
|
-
return dDisplay + hDisplay + mDisplay + sDisplay;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
exports.parseQuestionJSON = function(data, errors_to_title, vsprint_errors) {
|
|
188
|
-
|
|
189
|
-
// Strip unicode null-terminated-string control characters if there are any.
|
|
190
|
-
// These seem to be stripped already if we got data via The Graph, and only passed to us on RPC.
|
|
191
|
-
data = data.replace(/\u0000/g, "");
|
|
192
|
-
|
|
193
|
-
var question_json;
|
|
194
|
-
try {
|
|
195
|
-
question_json = JSON.parse(data);
|
|
196
|
-
} catch(e) {
|
|
197
|
-
question_json = {
|
|
198
|
-
'title': '[Badly formatted question]: ' + data,
|
|
199
|
-
'type': 'broken-question',
|
|
200
|
-
'errors': {"json_parse_failed": true}
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
if (question_json['outcomes'] && question_json['outcomes'].length > QUESTION_MAX_OUTCOMES) {
|
|
205
|
-
if (!question_json['errors']) question_json['errors'] = {};
|
|
206
|
-
question_json['errors']['too_many_outcomes'] = true;
|
|
207
|
-
}
|
|
208
|
-
if ('type' in question_json && question_json['type'] == 'datetime' && 'precision' in question_json) {
|
|
209
|
-
if (!(['Y', 'm', 'd', 'H', 'i', 's'].includes(question_json['precision']))) {
|
|
210
|
-
if (!question_json['errors']) question_json['errors'] = {};
|
|
211
|
-
question_json['errors']['invalid_precision'] = true;
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
if (vsprint_errors && vsprint_errors['suspicious_extra_data']) {
|
|
216
|
-
if (!question_json['errors']) question_json['errors'] = {};
|
|
217
|
-
question_json['errors']['suspicious_extra_data'] = true;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// If errors_to_title is specified, we add any error message to the title to make sure we don't lose it
|
|
221
|
-
if (errors_to_title) {
|
|
222
|
-
if ('errors' in question_json) {
|
|
223
|
-
const prependers = {
|
|
224
|
-
'invalid_precision': 'Invalid date format',
|
|
225
|
-
'too_many_outcomes': 'Too many outcomes',
|
|
226
|
-
}
|
|
227
|
-
for (const e in question_json['errors']) {
|
|
228
|
-
if (e in prependers) {
|
|
229
|
-
question_json['title'] = '['+prependers[e]+'] ' + question_json['title'];
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
if (!question_json['format']) {
|
|
236
|
-
question_json['format'] = 'text/plain';
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
if (question_json['format'] == 'text/plain') {
|
|
240
|
-
question_json['title_text'] = question_json['title'];
|
|
241
|
-
} else if (question_json['format'] == 'text/markdown') {
|
|
242
|
-
try{
|
|
243
|
-
const safeMarkdown = DOMPurify.sanitize(question_json['title'], { USE_PROFILES: {html: false}});
|
|
244
|
-
if (safeMarkdown !== question_json['title']) {
|
|
245
|
-
if (!question_json['errors']) question_json['errors'] = {};
|
|
246
|
-
question_json['errors']['unsafe_markdown'] = true;
|
|
247
|
-
} else {
|
|
248
|
-
question_json['title_html'] = marked.parse(safeMarkdown).replace(/<img.*src=\"(.*?)\".*alt=\"(.*?)\".*\/?>/, '<a href="$1">$2</a>');
|
|
249
|
-
question_json['title_text'] = convert(question_json['title_html'], {
|
|
250
|
-
selectors: [
|
|
251
|
-
{selector: 'h1', options: { uppercase: false }},
|
|
252
|
-
{selector: 'h2', options: { uppercase: false }},
|
|
253
|
-
{selector: 'hr', format: 'skip'}
|
|
254
|
-
]
|
|
255
|
-
});
|
|
256
|
-
}
|
|
257
|
-
} catch(e){
|
|
258
|
-
if (!question_json['errors']) question_json['errors'] = {};
|
|
259
|
-
question_json['errors']['markdown_parse_failed'] = true
|
|
260
|
-
}
|
|
261
|
-
} else {
|
|
262
|
-
if (!question_json['errors']) question_json['errors'] = {};
|
|
263
|
-
question_json['errors']['invalid_format'] = true;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
// If errors_to_title is specified, we add any error message to the title to make sure we don't lose it
|
|
267
|
-
if (errors_to_title) {
|
|
268
|
-
if ('errors' in question_json) {
|
|
269
|
-
const prependers = {
|
|
270
|
-
'invalid_format': 'Invalid format',
|
|
271
|
-
'unsafe_markdown': 'Unsafe markdown',
|
|
272
|
-
'markdown_parse_failed': 'Bad markdown parse',
|
|
273
|
-
'suspicious_extra_data': 'Suspicious Extra Data',
|
|
274
|
-
}
|
|
275
|
-
for (const e in question_json['errors']) {
|
|
276
|
-
if (e in prependers) {
|
|
277
|
-
question_json['title'] = '['+prependers[e]+'] ' + question_json['title'];
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
return question_json;
|
|
284
|
-
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
exports.populatedJSONForTemplate = function(template, question, errors_to_title) {
|
|
288
|
-
var qbits = question.split(module.exports.delimiter());
|
|
289
|
-
var interpolated = vsprintf(template, qbits);
|
|
290
|
-
|
|
291
|
-
var vsprint_errors = null;
|
|
292
|
-
if (question != '') {
|
|
293
|
-
var tweak_question = question + 'x';
|
|
294
|
-
var qbits2 = tweak_question.split(module.exports.delimiter());
|
|
295
|
-
var interpolated2 = vsprintf(template, qbits2);
|
|
296
|
-
if (interpolated == interpolated2) {
|
|
297
|
-
// console.log('suspicious_extra_data: ', question, template);
|
|
298
|
-
vsprint_errors = {'suspicious_extra_data': true};
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
//console.log('pp', template);
|
|
302
|
-
//console.log('qbits', qbits);
|
|
303
|
-
//console.log('resulting template', interpolated);
|
|
304
|
-
return module.exports.parseQuestionJSON(interpolated, errors_to_title, vsprint_errors);
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
// Encode text, assuming the template has placeholders in the order specified in the params.
|
|
308
|
-
// Additional information about the template can be passed in as template_definitions.
|
|
309
|
-
// If not specified, we'll assume it works like our standard built-in templates.
|
|
310
|
-
exports.encodeCustomText = function(params) {
|
|
311
|
-
|
|
312
|
-
var items = [];
|
|
313
|
-
for (const p in params) {
|
|
314
|
-
var val = params[p];
|
|
315
|
-
if (typeof val === 'string') {
|
|
316
|
-
// Stringify puts quotation marks around the string, so strip them
|
|
317
|
-
val = JSON.stringify(val).replace(/^"|"$/g, '');
|
|
318
|
-
} else if (typeof val === 'object') {
|
|
319
|
-
// An array of values should be stringified as a JSON array.
|
|
320
|
-
// However template should do "outcomes: [%s]" instead of "outcomes: %s".
|
|
321
|
-
// In that case strip the closing brackets.
|
|
322
|
-
val = JSON.stringify(val).replace(/^\[/, '').replace(/\]$/, '');
|
|
323
|
-
} else {
|
|
324
|
-
val = null;
|
|
325
|
-
}
|
|
326
|
-
items.push(val);
|
|
327
|
-
}
|
|
328
|
-
const ret = items.join(module.exports.delimiter());
|
|
329
|
-
return ret;
|
|
330
|
-
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
exports.guessTemplateConfig = function(template) {
|
|
334
|
-
// Use the hash of the template as a temporary placeholder
|
|
335
|
-
// Since you can't hash yourself we can be confident this won't collide.
|
|
336
|
-
const placeholder = '0x' + ethereumjs_abi.soliditySHA3(['string'], [template]).toString('hex');
|
|
337
|
-
const arr_placeholder = '0x' + ethereumjs_abi.soliditySHA3(['string'], [template + "_arr"]).toString('hex');
|
|
338
|
-
var pl_arr = new Array(TEMPLATE_MAX_PLACEHOLDERS);
|
|
339
|
-
pl_arr.fill(placeholder);
|
|
340
|
-
var interpolated = vsprintf(template, pl_arr);
|
|
341
|
-
|
|
342
|
-
// Quote the placeholders in any arrays that didn't originally have quotes
|
|
343
|
-
interpolated = interpolated.replaceAll('['+placeholder+']', '"'+arr_placeholder+'"');
|
|
344
|
-
|
|
345
|
-
const fake_json = module.exports.parseQuestionJSON(interpolated, false);
|
|
346
|
-
|
|
347
|
-
/*
|
|
348
|
-
Meta example:
|
|
349
|
-
{
|
|
350
|
-
'labels': {'dog': 'Dogs', 'cat': 'Cats'},
|
|
351
|
-
'tags': {'2': 'dog'}
|
|
352
|
-
'defaults': {'lang': 'en_US'}
|
|
353
|
-
}
|
|
354
|
-
*/
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
// If there's a section called __META, use that for titling columns etc
|
|
358
|
-
var meta = '__META' in fake_json ? fake_json['__META'] : {};
|
|
359
|
-
var labels = 'labels' in meta ? meta['labels'] : {};
|
|
360
|
-
|
|
361
|
-
var fields = {};
|
|
362
|
-
for (const k in fake_json) {
|
|
363
|
-
if (k == 'title_text' || k == 'title_html' || k == '__META') {
|
|
364
|
-
// Fields we add automatically
|
|
365
|
-
continue;
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
// We assume that every placeholder is either the value for a field, or contained in it.
|
|
369
|
-
// ie we handle
|
|
370
|
-
// "myfield": "%s"
|
|
371
|
-
// "myfield": "A %s and another %s."
|
|
372
|
-
// "myfield": [%s]
|
|
373
|
-
|
|
374
|
-
var fdef = {};
|
|
375
|
-
fdef['label'] = (k in labels) ? labels[k] : k;
|
|
376
|
-
const regexp = new RegExp('('+placeholder+'|'+arr_placeholder+')');
|
|
377
|
-
//const regexp = new RegExp('('+placeholder+')');
|
|
378
|
-
const bits = fake_json[k].split(regexp);
|
|
379
|
-
// console.log(bits);
|
|
380
|
-
var parts = [];
|
|
381
|
-
var part_i = 0;
|
|
382
|
-
for(const b in bits) {
|
|
383
|
-
if (bits[b] == '') {
|
|
384
|
-
continue;
|
|
385
|
-
}
|
|
386
|
-
if (bits[b] == placeholder || bits[b] == arr_placeholder) {
|
|
387
|
-
let part_label = k + '_' + part_i;
|
|
388
|
-
if (part_label in labels) {
|
|
389
|
-
part_label = labels[part_label];
|
|
390
|
-
}
|
|
391
|
-
if (bits[b] == placeholder) {
|
|
392
|
-
parts.push({'part': 'parameter', 'part_index': part_i, 'label': part_label})
|
|
393
|
-
} else {
|
|
394
|
-
parts.push({'part': 'array_parameter', 'part_index': part_i, 'label': part_label})
|
|
395
|
-
}
|
|
396
|
-
part_i++;
|
|
397
|
-
} else {
|
|
398
|
-
parts.push({'part': 'text', 'value': bits[b]});
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
fdef['parts'] = parts;
|
|
402
|
-
fields[k] = fdef;
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
var tags = {};
|
|
406
|
-
if ('tags' in meta) {
|
|
407
|
-
tags = meta['tags']
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
return {
|
|
411
|
-
'fields': fields,
|
|
412
|
-
'tags': tags
|
|
413
|
-
};
|
|
414
|
-
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
exports.encodeText = function(qtype, txt, outcomes, category, lang) {
|
|
418
|
-
var def = {};
|
|
419
|
-
def['title'] = txt;
|
|
420
|
-
if (qtype == 'single-select' || qtype == 'multiple-select') {
|
|
421
|
-
def['outcomes'] = outcomes;
|
|
422
|
-
}
|
|
423
|
-
def['category'] = category;
|
|
424
|
-
def['lang'] = lang;
|
|
425
|
-
|
|
426
|
-
return module.exports.encodeCustomText(def);
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
// A value used to denote that the question is invalid or can't be answered
|
|
430
|
-
exports.getInvalidValue = function(question_json) {
|
|
431
|
-
// equivalent to -1 in twos complement
|
|
432
|
-
return '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff';
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
// A value used to denote that the question is invalid or can't be answered
|
|
436
|
-
exports.getAnsweredTooSoonValue = function(question_json) {
|
|
437
|
-
// equivalent to -2 in twos complement
|
|
438
|
-
return '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe';
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
exports.getLanguage = function(question_json) {
|
|
442
|
-
if ( typeof question_json['lang'] == 'undefined' || question_json['lang'] == '') {
|
|
443
|
-
return 'en_US';
|
|
444
|
-
}
|
|
445
|
-
return question_json['lang'];
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
exports.hasInvalidOption = function(question_json, contract_version) {
|
|
449
|
-
return !('has_invalid' in question_json && !question_json['has_invalid']);
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
exports.hasAnsweredTooSoonOption = function(question_json, contract_version) {
|
|
453
|
-
const bits = contract_version.split('.');
|
|
454
|
-
return (parseInt(bits[0]) >= 3);
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
exports.getAnswerString = function(question_json, answer) {
|
|
458
|
-
if (answer === null) {
|
|
459
|
-
return 'null';
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
if (answer == module.exports.getInvalidValue(question_json)) {
|
|
463
|
-
return 'Invalid';
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
if (answer == module.exports.getAnsweredTooSoonValue(question_json)) {
|
|
467
|
-
return 'Answered too soon';
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
var label = '';
|
|
471
|
-
switch (question_json['type']) {
|
|
472
|
-
case 'uint':
|
|
473
|
-
label = module.exports.bytes32ToString(answer, question_json);
|
|
474
|
-
break;
|
|
475
|
-
case 'int':
|
|
476
|
-
label = module.exports.bytes32ToString(answer, question_json);
|
|
477
|
-
break;
|
|
478
|
-
case 'hash':
|
|
479
|
-
label = module.exports.bytes32ToString(answer, question_json);
|
|
480
|
-
break;
|
|
481
|
-
case 'bool':
|
|
482
|
-
if (new BigNumber(answer).toNumber() === 1) {
|
|
483
|
-
label = 'Yes';
|
|
484
|
-
} else if (new BigNumber(answer).toNumber() === 0) {
|
|
485
|
-
label = 'No';
|
|
486
|
-
}
|
|
487
|
-
break;
|
|
488
|
-
case 'single-select':
|
|
489
|
-
if (typeof question_json['outcomes'] !== 'undefined' && question_json['outcomes'].length > 0) {
|
|
490
|
-
var idx = new BigNumber(answer).toNumber();
|
|
491
|
-
label = question_json['outcomes'][idx];
|
|
492
|
-
}
|
|
493
|
-
break;
|
|
494
|
-
case 'multiple-select':
|
|
495
|
-
if (typeof question_json['outcomes'] !== 'undefined' && question_json['outcomes'].length > 0) {
|
|
496
|
-
var answer_bits = new BigNumber(answer).toString(2);
|
|
497
|
-
var length = answer_bits.length;
|
|
498
|
-
var entries = [];
|
|
499
|
-
for (var i = question_json['outcomes'].length - 1; i >= 0; i--) {
|
|
500
|
-
if (answer_bits[i] === '1') {
|
|
501
|
-
var idx = answer_bits.length - 1 - i;
|
|
502
|
-
entries.push(question_json['outcomes'][idx]);
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
return entries.join(' / ');
|
|
506
|
-
}
|
|
507
|
-
break;
|
|
508
|
-
case 'datetime':
|
|
509
|
-
let precision = 'd';
|
|
510
|
-
if ('precision' in question_json && ['Y', 'm', 'd', 'H', 'i', 's'].includes(question_json['precision'])) {
|
|
511
|
-
precision = question_json['precision'];
|
|
512
|
-
}
|
|
513
|
-
let ts = parseInt(module.exports.bytes32ToString(answer, question_json));
|
|
514
|
-
let dateObj = new Date(ts * 1000);
|
|
515
|
-
|
|
516
|
-
const year = dateObj.getUTCFullYear();
|
|
517
|
-
const month = dateObj.getUTCMonth() + 1;
|
|
518
|
-
const date = dateObj.getUTCDate();
|
|
519
|
-
const hour = dateObj.getUTCHours();
|
|
520
|
-
const min = dateObj.getUTCMinutes();
|
|
521
|
-
const sec = dateObj.getUTCSeconds();
|
|
522
|
-
|
|
523
|
-
// We need whatever the precision states, plus anything above
|
|
524
|
-
const needy = true;
|
|
525
|
-
const needm = (precision != 'Y');
|
|
526
|
-
const needd = needm && (precision != 'm');
|
|
527
|
-
const needH = needd && (precision != 'd');
|
|
528
|
-
const needi = needH && (precision != 'H');
|
|
529
|
-
const needs = needi && (precision != 'i');
|
|
530
|
-
|
|
531
|
-
// If anything is set then we've got it.
|
|
532
|
-
// We also consider that anything required by the precision is there, but set to 0
|
|
533
|
-
const hass = needs || sec > 0;
|
|
534
|
-
const hasi = needi || hass || min > 0;
|
|
535
|
-
const hasH = needH || hasi || hour > 0;
|
|
536
|
-
const hasd = needd || hasH || date > 1;
|
|
537
|
-
const hasm = needm || hasd || month > 1;
|
|
538
|
-
const hasy = true;
|
|
539
|
-
|
|
540
|
-
// We'll show an invalid warning if we've got a more precise date than the precision demands
|
|
541
|
-
let invalid = false;
|
|
542
|
-
if (!needm && hasm) invalid = true;
|
|
543
|
-
if (!needd && hasd) invalid = true;
|
|
544
|
-
if (!needH && hasH) invalid = true;
|
|
545
|
-
if (!needi && hasi) invalid = true;
|
|
546
|
-
if (!needs && hass) invalid = true;
|
|
547
|
-
|
|
548
|
-
if (invalid) {
|
|
549
|
-
label = '[Invalid datetime]: ';
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
function pad2(n) { return ("0" + n).slice(-2); }
|
|
553
|
-
|
|
554
|
-
if (hasy) {
|
|
555
|
-
label += year;
|
|
556
|
-
}
|
|
557
|
-
if (hasm) {
|
|
558
|
-
label += '-'+pad2(month);
|
|
559
|
-
}
|
|
560
|
-
if (hasd) {
|
|
561
|
-
label += '-'+pad2(date);
|
|
562
|
-
}
|
|
563
|
-
if (hasH) {
|
|
564
|
-
label += ' '+pad2(hour);
|
|
565
|
-
}
|
|
566
|
-
if (hasi) {
|
|
567
|
-
label += ':'+pad2(min);
|
|
568
|
-
} else if (hasH) {
|
|
569
|
-
// "2021-12-23 11" without the minutes at the end is hard to understand so add "hr"
|
|
570
|
-
label += 'hr';
|
|
571
|
-
}
|
|
572
|
-
if (hass) {
|
|
573
|
-
label += ':'+pad2(sec);
|
|
574
|
-
}
|
|
575
|
-
break;
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
return label;
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
exports.commitmentID = function(question_id, answer_hash, bond) {
|
|
582
|
-
const bond_hex = (typeof(bond) === 'string') ? bond : bond.toString(16);
|
|
583
|
-
return "0x" + ethereumjs_abi.soliditySHA3(
|
|
584
|
-
["uint256", "uint256", "uint256"],
|
|
585
|
-
[ new BN(question_id.replace(/^0x/, ''), 16), new BN(answer_hash.replace(/^0x/, ''), 16), new BN(bond_hex.replace(/^0x/, ''), 16)]
|
|
586
|
-
).toString('hex');
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
exports.answerHash = function(answer_plaintext, nonce) {
|
|
590
|
-
return "0x" + ethereumjs_abi.soliditySHA3(
|
|
591
|
-
["uint256", "uint256"],
|
|
592
|
-
[ new BN(answer_plaintext.replace(/^0x/, ''), 16), new BN(nonce.replace(/^0x/, ''), 16)]
|
|
593
|
-
).toString('hex');
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
exports.shortDisplayQuestionID = function(question_id) {
|
|
597
|
-
// Question ID may or may not have the contract address prepended
|
|
598
|
-
const bits = question_id.split('-');
|
|
599
|
-
const qid = bits[bits.length-1];
|
|
600
|
-
return qid.substring(2,9) + '...' + qid.slice(-7);
|
|
601
|
-
}
|
|
1
|
+
// CJS compatibility shim — real implementation is in dist/cjs/formatters/question.js (compiled from src/formatters/question.ts)
|
|
2
|
+
'use strict';
|
|
3
|
+
module.exports = require('../dist/cjs/formatters/question.js');
|
package/formatters/template.js
CHANGED
|
@@ -1,28 +1,3 @@
|
|
|
1
|
+
// CJS compatibility shim — real implementation is in dist/cjs/formatters/template.js (compiled from src/formatters/template.ts)
|
|
1
2
|
'use strict';
|
|
2
|
-
|
|
3
|
-
const TEMPLATE_CONFIG = require('@reality.eth/contracts/config/templates.json');
|
|
4
|
-
|
|
5
|
-
exports.defaultTemplateIDForType = function(template_type) {
|
|
6
|
-
return TEMPLATE_CONFIG.base_ids[template_type];
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
exports.defaultTemplateForType = function(template_type) {
|
|
10
|
-
return TEMPLATE_CONFIG.content[module.exports.defaultTemplateIDForType(template_type)];
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
exports.preloadedTemplateContents = function() {
|
|
14
|
-
return TEMPLATE_CONFIG.content;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
exports.encodeText = function(qtype, txt, outcomes, category) {
|
|
18
|
-
var qtext = JSON.stringify(txt).replace(/^"|"$/g, '');
|
|
19
|
-
//console.log('using template_id', template_id);
|
|
20
|
-
if (qtype == 'single-select' || qtype == 'multiple-select') {
|
|
21
|
-
var outcome_str = JSON.stringify(outcomes).replace(/^\[/, '').replace(/\]$/, '');
|
|
22
|
-
//console.log('made outcome_str', outcome_str);
|
|
23
|
-
qtext = qtext + QUESTION_DELIMITER + outcome_str;
|
|
24
|
-
//console.log('made qtext', qtext);
|
|
25
|
-
}
|
|
26
|
-
qtext = qtext + QUESTION_DELIMITER + category;
|
|
27
|
-
return qtext;
|
|
28
|
-
}
|
|
3
|
+
module.exports = require('../dist/cjs/formatters/template.js');
|