@truenetworkio/sdk
Version:
True Network SDK is the abstracted interface for interacting with True Network nodes.
519 lines (518 loc) • 21.2 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Schema = exports.stringToSchemaType = exports.Text = exports.Bool = exports.Hash = exports.F64 = exports.F32 = exports.I64 = exports.U64 = exports.I32 = exports.U32 = exports.I16 = exports.U16 = exports.I8 = exports.U8 = exports.Char = exports.SchemaType = void 0;
const hashing_1 = require("../utils/hashing");
const state_1 = require("../pallets/credentials/state");
const extrinsic_1 = require("../pallets/credentials/extrinsic");
const __1 = require("..");
class SchemaType {
hexToLittleEndianNumber(hex) {
// Remove '0x' prefix if present
hex = hex.replace('0x', '');
// Ensure the hex string has the correct length
while (hex.length < this.sizeInBytes * 2) {
hex = hex + '0';
}
// Convert to little-endian
const bytes = [];
for (let i = 0; i < hex.length; i += 2) {
bytes.push(parseInt(hex.substr(i, 2), 16));
}
// Create a DataView to handle the conversion
const buffer = new ArrayBuffer(this.sizeInBytes);
const view = new DataView(buffer);
// Set bytes in little-endian order
bytes.forEach((byte, index) => {
view.setUint8(index, byte);
});
// Read the value based on size
switch (this.sizeInBytes) {
case 1: return view.getUint8(0);
case 2: return view.getUint16(0, true);
case 4: return view.getUint32(0, true);
default: return view.getUint32(0, true);
}
}
hexToLittleEndianBigInt(hex) {
// Remove '0x' prefix if present
hex = hex.replace('0x', '');
// Ensure the hex string has the correct length
while (hex.length < this.sizeInBytes * 2) {
hex = hex + '0';
}
// Convert to little-endian
const bytes = [];
for (let i = 0; i < hex.length; i += 2) {
bytes.push(parseInt(hex.substr(i, 2), 16));
}
// Create a DataView to handle the conversion
const buffer = new ArrayBuffer(this.sizeInBytes);
const view = new DataView(buffer);
// Set bytes in little-endian order
bytes.forEach((byte, index) => {
view.setUint8(index, byte);
});
return view.getBigUint64(0, true);
}
}
exports.SchemaType = SchemaType;
class CharType extends SchemaType {
constructor() {
super(...arguments);
this.sizeInBytes = 1;
this.id = 0;
}
isValid(v) { return v.length === 1; }
serialize(v) { return (0, hashing_1.toLittleEndianHex)(v.charCodeAt(0), this.sizeInBytes); }
deserialize(v) { return String.fromCharCode(parseInt(v, 16)); }
}
class U8Type extends SchemaType {
constructor() {
super(...arguments);
this.sizeInBytes = 1;
this.id = 1;
}
isValid(v) { return Number.isInteger(v) && v >= 0 && v <= 255; }
serialize(v) { return (0, hashing_1.toLittleEndianHex)(v, this.sizeInBytes); }
deserialize(v) { return this.hexToLittleEndianNumber(v); }
}
class I8Type extends SchemaType {
constructor() {
super(...arguments);
this.sizeInBytes = 1;
this.id = 2;
}
isValid(v) { return Number.isInteger(v) && v >= -128 && v <= 127; }
serialize(v) { return (0, hashing_1.toLittleEndianHex)(v & 0xFF, this.sizeInBytes); }
deserialize(v) {
const num = this.hexToLittleEndianNumber(v);
return num > 127 ? num - 256 : num;
}
}
class U16Type extends SchemaType {
constructor() {
super(...arguments);
this.sizeInBytes = 2;
this.id = 3;
}
isValid(v) { return Number.isInteger(v) && v >= 0 && v <= 65535; }
serialize(v) { return (0, hashing_1.toLittleEndianHex)(v, this.sizeInBytes); }
deserialize(v) { return this.hexToLittleEndianNumber(v); }
}
class I16Type extends SchemaType {
constructor() {
super(...arguments);
this.sizeInBytes = 2;
this.id = 4;
}
isValid(v) { return Number.isInteger(v) && v >= -32768 && v <= 32767; }
serialize(v) { return (0, hashing_1.toLittleEndianHex)(v & 0xFFFF, this.sizeInBytes); }
deserialize(v) {
const num = this.hexToLittleEndianNumber(v);
return num > 32767 ? num - 65536 : num;
}
}
class U32Type extends SchemaType {
constructor() {
super(...arguments);
this.sizeInBytes = 4;
this.id = 5;
}
isValid(v) { return Number.isInteger(v) && v >= 0 && v <= 4294967295; }
serialize(v) { return (0, hashing_1.toLittleEndianHex)(v, this.sizeInBytes); }
deserialize(v) { return this.hexToLittleEndianNumber(v); }
}
class I32Type extends SchemaType {
constructor() {
super(...arguments);
this.sizeInBytes = 4;
this.id = 6;
}
isValid(v) { return Number.isInteger(v) && v >= -2147483648 && v <= 2147483647; }
serialize(v) { return (0, hashing_1.toLittleEndianHex)(v >>> 0, this.sizeInBytes); }
deserialize(v) {
const num = this.hexToLittleEndianNumber(v);
return num > 2147483647 ? num - 4294967296 : num;
}
}
class U64Type extends SchemaType {
constructor() {
super(...arguments);
this.sizeInBytes = 8;
this.id = 7;
}
isValid(v) {
const bigIntValue = BigInt(v);
return bigIntValue >= BigInt(0) && bigIntValue <= BigInt("18446744073709551615");
}
serialize(v) { return (0, hashing_1.toLittleEndianHex)(BigInt(v), this.sizeInBytes); }
deserialize(v) {
return this.hexToLittleEndianBigInt(v);
}
}
class I64Type extends SchemaType {
constructor() {
super(...arguments);
this.sizeInBytes = 8;
this.id = 8;
}
isValid(v) {
const bigIntValue = BigInt(v);
return bigIntValue >= BigInt("-9223372036854775808") && bigIntValue <= BigInt("9223372036854775807");
}
serialize(v) { return (0, hashing_1.toLittleEndianHex)(BigInt(v) & BigInt("0xFFFFFFFFFFFFFFFF"), this.sizeInBytes); }
deserialize(v) {
const num = this.hexToLittleEndianBigInt(v);
return num > BigInt("9223372036854775807") ? num - BigInt("18446744073709551616") : num;
}
}
class F32Type extends SchemaType {
constructor() {
super(...arguments);
this.sizeInBytes = 4;
this.id = 9;
}
isValid(v) {
if (!Number.isFinite(v))
return false;
const min = -3.4028234663852886e+38;
const max = 3.4028234663852886e+38;
return v >= min && v <= max;
}
serialize(v) {
const buffer = new ArrayBuffer(4);
const view = new DataView(buffer);
view.setFloat32(0, v, true);
return "0x" + Array.from(new Uint8Array(buffer))
.map(b => b.toString(16).padStart(2, '0'))
.join('');
}
deserialize(v) {
// Remove '0x' prefix if present
v = v.replace('0x', '');
// Ensure the hex string has the correct length
while (v.length < this.sizeInBytes * 2) {
v = v + '0';
}
const bytes = [];
for (let i = 0; i < v.length; i += 2) {
bytes.push(parseInt(v.substr(i, 2), 16));
}
const buffer = new ArrayBuffer(4);
const view = new DataView(buffer);
bytes.forEach((byte, index) => {
view.setUint8(index, byte);
});
return view.getFloat32(0, true);
}
}
class F64Type extends SchemaType {
constructor() {
super(...arguments);
this.sizeInBytes = 8;
this.id = 10;
}
isValid(v) {
return !isNaN(v) && Number.isFinite(v);
}
serialize(v) {
const buffer = new ArrayBuffer(8);
const view = new DataView(buffer);
view.setFloat64(0, v, true);
return "0x" + Array.from(new Uint8Array(buffer))
.map(b => b.toString(16).padStart(2, '0'))
.join('');
}
deserialize(v) {
// Remove '0x' prefix if present
v = v.replace('0x', '');
// Ensure the hex string has the correct length
while (v.length < this.sizeInBytes * 2) {
v = v + '0';
}
const bytes = [];
for (let i = 0; i < v.length; i += 2) {
bytes.push(parseInt(v.substr(i, 2), 16));
}
const buffer = new ArrayBuffer(8);
const view = new DataView(buffer);
bytes.forEach((byte, index) => {
view.setUint8(index, byte);
});
return view.getFloat64(0, true);
}
}
class HashType extends SchemaType {
constructor() {
super(...arguments);
this.sizeInBytes = 32;
this.id = 11;
}
isValid(v) { return /^0x[a-fA-F0-9]{64}$/.test(v); }
serialize(v) { return v.slice(2); } // Remove '0x' prefix
deserialize(v) { return `0x${v}`; }
}
class BoolType extends SchemaType {
constructor() {
super(...arguments);
this.sizeInBytes = 1;
this.id = 12;
}
isValid(v) { return true; }
serialize(v) { return (0, hashing_1.toLittleEndianHex)(v ? 1 : 0, this.sizeInBytes); ; }
deserialize(v) { return parseInt(v, 16) == 1; }
}
class StringType extends SchemaType {
constructor() {
super(...arguments);
this.sizeInBytes = 128;
this.id = 13;
}
isValid(v) { return v.length < 128; }
serialize(v) { return v; }
deserialize(v) { return v; }
}
exports.Char = new CharType();
exports.U8 = new U8Type();
exports.I8 = new I8Type();
exports.U16 = new U16Type();
exports.I16 = new I16Type();
exports.U32 = new U32Type();
exports.I32 = new I32Type();
exports.U64 = new U64Type();
exports.I64 = new I64Type();
exports.F32 = new F32Type();
exports.F64 = new F64Type();
exports.Hash = new HashType();
exports.Bool = new BoolType();
exports.Text = new StringType();
const stringToSchemaType = (type) => {
switch (type) {
case 'Char':
return exports.Char;
case 'U8':
return exports.U8;
case 'I8':
return exports.I8;
case 'U16':
return exports.U16;
case 'I16':
return exports.I16;
case 'U32':
return exports.U32;
case 'I32':
return exports.I32;
case 'U64':
return exports.U64;
case 'I64':
return exports.I64;
case 'F32':
return exports.F32;
case 'F64':
return exports.F64;
case 'Hash':
return exports.Hash;
case 'Bool':
return exports.Bool;
case 'Text':
return exports.Text;
default:
return undefined;
}
};
exports.stringToSchemaType = stringToSchemaType;
class Schema {
// Set a default value for the Schema Object.
constructor(def) {
this.type = {};
this.def = def;
this.schemaHash = this.getSchemaHash();
}
static create(def) {
return new Schema(def);
}
getAttestations(api, address) {
return __awaiter(this, void 0, void 0, function* () {
const data = yield (0, state_1.getAttestationForSchema)(api.network, address, api.issuerHash, this);
if (!data)
throw Error("Attestation doesn't not exist.");
// Convert array data to structured schema object.
const response = [];
const sortedEntries = Object.entries(this.def).sort((a, b) => b[0].localeCompare(a[0]));
data.forEach((d, i) => {
let objs = {};
sortedEntries.forEach((entry, index) => {
const [key, schemaType] = entry;
const value = d[index];
// Convert the value to string if it's a number
const stringValue = typeof value === 'number' ? value.toString(16).padStart(schemaType.sizeInBytes * 2, '0') : value;
if (typeof stringValue !== 'string') {
throw new Error(`Unexpected data type for ${key}: ${typeof value}`);
}
objs[key] = schemaType.deserialize(stringValue);
});
response.push(objs);
});
return response;
});
}
getSortedEntries(item) {
return Object.entries(item).sort((a, b) => b[0].localeCompare(a[0]));
}
getEntries() {
return this.getSortedEntries(this.def);
}
// Returns the Schema Object to pass in the polkadot js api.
getSchemaObject() {
return this.getSortedEntries(this.def).map(item => [item[0], item[1].id]);
}
// Generate Schema Hash.
getSchemaHash() {
const encoder = new TextEncoder();
const bytes = [];
this.getSortedEntries(this.def).forEach(i => {
const bA0 = encoder.encode(i[0]);
const bA1 = (0, hashing_1.numberToUint8Array)(i[1].id);
// Concatenate the byte arrays to the bytes array
bytes.push(bA0);
bytes.push(bA1);
});
// Calculate the total length of bytes
const totalLength = bytes.reduce((acc, curr) => acc + curr.length, 0);
// Create a new Uint8Array with the correct length
const combinedBytes = new Uint8Array(totalLength);
// Copy the concatenated byte arrays to the combinedBytes array
let offset = 0;
bytes.forEach(byteArray => {
combinedBytes.set(byteArray, offset);
offset += byteArray.length;
});
return `0x${(0, hashing_1.bytesToBlakeTwo256Hash)(combinedBytes)}`;
}
ifExistAlready(api) {
return __awaiter(this, void 0, void 0, function* () {
return (0, state_1.checkIfSchemaExist)(api.network, this.schemaHash);
});
}
register(api) {
return __awaiter(this, void 0, void 0, function* () {
if (!api.issuerHash)
throw Error("Issuer Hash is not defined in TrueApi, either pass the hash in constructor or call api.registerIssuer(name: string).");
if (yield this.ifExistAlready(api)) {
// Schema already exist.
return this.schemaHash;
}
return yield (0, extrinsic_1.createSchema)(api.network, api.account, api.issuerHash, this);
});
}
validate(data) {
for (const [key, value] of Object.entries(data)) {
if (!this.def[key].isValid(value)) {
throw new Error(`Invalid value for ${key}: ${value}`);
}
}
}
attest(api, user, attestation) {
return __awaiter(this, void 0, void 0, function* () {
this.validate(attestation);
// Check if issuer hash exists in the api.
if (!api.issuerHash)
throw Error("issuerHash property does not exist on TrueApi, try registering an issuer.");
// Serialize the attestation values.
const values = this.getSortedEntries(attestation).map(([key, value]) => {
return this.def[key].serialize(value);
});
// Check if schema exists, if not register & attest.
if (!(yield this.ifExistAlready(api))) {
// Do a combined transaction of register & attest on-chain.
const schemaTx = yield (0, extrinsic_1.createSchemaTx)(api.network, api.account, api.issuerHash, this);
const attestationTx = yield (0, extrinsic_1.createAttestationTx)(api.network, api.account, api.issuerHash, this, user, values);
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
yield api.network.tx.utility.batch([schemaTx, attestationTx]).signAndSend(api.account, ({ status, events }) => {
events.forEach(({ event: { method } }) => {
if (method == 'ExtrinsicFailed') {
reject(Error(`Transaction failed, error attesting on-chain for the users. \ntx: ${status.hash}`));
}
});
let attestationId = -1;
if (status.isInBlock) {
console.log(`\nTransaction is inBlock at blockHash ${status.asInBlock}`);
events.forEach(({ event: { method, data } }) => {
if (method == 'AttestationCreated') {
console.log('Attestation Created: InBlock');
const jsonData = data.toJSON();
if (jsonData) {
attestationId = jsonData[3];
}
}
if (method == 'ExtrinsicFailed') {
reject(Error(`\nTransaction failed, error attesting on-chain for the user. \ntx: ${status.hash}`));
}
});
resolve({
attestationId,
prismUrl: `${__1.prismUrl}/query/${status.asInBlock.toString()}`,
transaction: {
hash: status.asInBlock.toString(),
explorerUrl: `https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Framan.truenetwork.io%2Fws#/explorer/query/${status.asInBlock.toString()}`,
events: events
}
});
}
});
}));
}
return yield (0, extrinsic_1.createAttestation)(api.network, api.account, api.issuerHash, this, user, values);
});
}
updateAttestation(api, user, attestationId, attestation) {
return __awaiter(this, void 0, void 0, function* () {
this.validate(attestation);
// Check if issuer hash exists in the api.
if (!api.issuerHash)
throw Error("issuerHash property does not exist on TrueApi, try registering an issuer.");
// Serialize the attestation values.
const values = this.getSortedEntries(attestation).map(([key, value]) => {
return this.def[key].serialize(value);
});
// Check if schema exists, if not register & attest.
if (!(yield this.ifExistAlready(api))) {
// Do a combined transaction of register & attest on-chain.
const schemaTx = yield (0, extrinsic_1.createSchemaTx)(api.network, api.account, api.issuerHash, this);
const attestationTx = yield (0, extrinsic_1.createAttestationTx)(api.network, api.account, api.issuerHash, this, user, values);
return new Promise((resolve, _) => __awaiter(this, void 0, void 0, function* () {
yield api.network.tx.utility.batch([schemaTx, attestationTx]).signAndSend(api.account, ({ status, events }) => {
events.forEach(({ event: { method } }) => {
if (method == 'ExtrinsicFailed') {
throw Error(`\nTransaction failed, error attesting on-chain for the users. \ntx: ${status.hash}`);
}
});
if (status.isInBlock) {
console.log(`\nTransaction is inBlock at blockHash ${status.asInBlock}`);
resolve({
attestationId,
prismUrl: `${__1.prismUrl}/query/${status.asInBlock.toString()}`,
transaction: {
hash: status.asInBlock.toString(),
explorerUrl: `https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Framan.truenetwork.io%2Fws#/explorer/query/${status.asInBlock.toString()}`,
events: events
}
});
}
});
}));
}
return yield (0, extrinsic_1.updateAttestation)(api.network, api.account, api.issuerHash, this, user, attestationId, values);
});
}
}
exports.Schema = Schema;