@ylide/everscale
Version:
Ylide Protocol SDK implementation for EverScale blockchain
887 lines (818 loc) • 25.1 kB
text/typescript
import SmartBuffer from '@ylide/smart-buffer';
import { EverscaleStandaloneClient } from 'everscale-standalone-client';
import core from 'everscale-standalone-client/core';
import { Address, ProviderRpcClient } from 'everscale-inpage-provider';
import nacl from 'tweetnacl';
import {
AbstractBlockchainController,
IMessage,
IMessageContent,
IMessageCorruptedContent,
MessageContentFailure,
unpackSymmetricalyEncryptedData,
IExtraEncryptionStrateryBulk,
IExtraEncryptionStrateryEntry,
MessageKey,
PublicKey,
PublicKeyType,
packSymmetricalyEncryptedData,
BlockchainControllerFactory,
Uint256,
hexToUint256,
ISourceSubject,
BlockchainSourceType,
AbstractNameService,
} from '@ylide/sdk';
import {
DEV_BROADCASTER_ADDRESS,
DEV_MAILER_ADDRESS,
DEV_REGISTRY_ADDRESS,
BROADCASTER_EVERSCALE_ADDRESS,
MAILER_EVERSCALE_ADDRESS,
REGISTRY_EVERSCALE_ADDRESS,
BROADCASTER_VENOM_ADDRESS,
MAILER_VENOM_ADDRESS,
REGISTRY_VENOM_ADDRESS,
} from '../misc/constants';
import { IEverscaleContentMessageBody, IEverscaleMessage, uint256ToAddress } from '../misc';
import { getContractMessagesQuery } from '../misc';
import { GqlSender } from '../misc/GqlSender';
import { initAsync, encrypt, generate_ephemeral, get_public_key } from '../encrypt';
import moment from 'moment';
import {
decodeAddressToPublicKeyMessageBody_old,
decodeBroadcastMessageBody,
decodeContentMessageBody,
decodePushMessageBody,
} from '../contracts/contractUtils';
export class EverscaleBlockchainController extends AbstractBlockchainController {
ever: ProviderRpcClient;
gql: GqlSender;
readonly everscaleEncryptCore = initAsync();
readonly MESSAGES_FETCH_LIMIT = 50;
readonly mailerContractAddress: string;
readonly broadcasterContractAddress: string;
readonly registryContractAddress: string;
private readonly everscaleMainnetEndpoints = [
'https://mainnet.evercloud.dev/695e40eeac6b4e3fa4a11666f6e0d6af/graphql',
];
private readonly venomTestnetEndpoints = ['https://gql-testnet.venom.foundation/graphql'];
constructor(
private options: {
type?: 'everscale-mainnet' | 'venom-testnet';
dev?: boolean;
mailerContractAddress?: string;
broadcasterContractAddress?: string;
registryContractAddress?: string;
endpoints?: string[];
} = {},
) {
super(options);
if (typeof options.type === 'undefined') {
throw new Error('You must provide network type for Everscale controller');
}
if (options.endpoints) {
this.gql = new GqlSender({
endpoints: options.endpoints,
local: false,
});
} else if (options.dev) {
this.gql = new GqlSender({
endpoints: ['localhost'],
local: true,
});
} else {
this.gql = new GqlSender({
endpoints:
options.type === 'everscale-mainnet' ? this.everscaleMainnetEndpoints : this.venomTestnetEndpoints,
local: false,
});
}
this.ever = new ProviderRpcClient({
forceUseFallback: true,
fallback: () =>
EverscaleStandaloneClient.create({
connection: options.dev
? 'local'
: {
id: 1,
group: 'mainnet',
type: 'graphql',
data: {
local: false,
endpoints:
options.type === 'everscale-mainnet'
? this.everscaleMainnetEndpoints
: this.venomTestnetEndpoints,
},
},
}),
});
this.mailerContractAddress =
options.mailerContractAddress ||
(options.dev
? DEV_MAILER_ADDRESS
: options.type === 'venom-testnet'
? MAILER_VENOM_ADDRESS
: MAILER_EVERSCALE_ADDRESS);
this.broadcasterContractAddress =
options.broadcasterContractAddress ||
(options.dev
? DEV_BROADCASTER_ADDRESS
: options.type === 'venom-testnet'
? BROADCASTER_VENOM_ADDRESS
: BROADCASTER_EVERSCALE_ADDRESS);
this.registryContractAddress =
options.registryContractAddress ||
(options.dev
? DEV_REGISTRY_ADDRESS
: options.type === 'venom-testnet'
? REGISTRY_VENOM_ADDRESS
: REGISTRY_EVERSCALE_ADDRESS);
}
isReadingBySenderAvailable(): boolean {
return false;
}
defaultNameService(): AbstractNameService | null {
return null;
}
async init(): Promise<void> {
// np
}
async getBalance(address: string): Promise<string> {
return this.ever.getBalance(new Address(address));
}
getDefaultMailerAddress() {
return this.mailerContractAddress;
}
async getRecipientReadingRules(address: Uint256): Promise<any> {
return [];
}
private async getPublicKeyByAddress(address: string): Promise<Uint8Array | null> {
await core.ensureNekotonLoaded();
const messages = await this.gql.queryContractMessages(address, this.registryContractAddress);
if (messages.length) {
const val = decodeAddressToPublicKeyMessageBody_old(messages[0].body);
if (val) {
return val;
} else {
return null;
}
} else {
return null;
}
}
async extractPublicKeyFromAddress(address: string): Promise<PublicKey | null> {
const rawKey = await this.getPublicKeyByAddress(':' + address.split(':')[1]);
if (!rawKey) {
return null;
}
return PublicKey.fromBytes(PublicKeyType.YLIDE, rawKey);
}
private async _retrieveMessageHistoryByTime(
contractAddress: string,
subject: ISourceSubject,
fromTimestamp?: number,
toTimestamp?: number,
limit?: number,
): Promise<IMessage[]> {
await core.ensureNekotonLoaded();
if (!contractAddress) {
contractAddress = this.getDefaultMailerAddress();
}
const events = await this.queryMessagesList(contractAddress, subject, limit, {
fromDate: fromTimestamp,
toDate: toTimestamp,
});
const result = events.map(m =>
subject.type === BlockchainSourceType.DIRECT
? this.formatPushMessage(m)
: this.formatBroadcastMessage(subject.sender!, m),
);
return result.filter(
r =>
(!fromTimestamp || r.$$blockchainMetaDontUseThisField.block.timestamp > fromTimestamp) &&
(!toTimestamp || r.$$blockchainMetaDontUseThisField.block.timestamp <= toTimestamp),
);
}
private async _retrieveMessageHistoryByBounds(
contractAddress: string,
subject: ISourceSubject,
fromMessage?: IMessage,
toMessage?: IMessage,
limit?: number,
): Promise<IMessage[]> {
await core.ensureNekotonLoaded();
const events = await this.queryMessagesList(contractAddress, subject, limit, {
fromMessage: fromMessage?.$$blockchainMetaDontUseThisField,
toMessage: toMessage?.$$blockchainMetaDontUseThisField,
});
const result = events.map(m =>
subject.type === BlockchainSourceType.DIRECT
? this.formatPushMessage(m)
: this.formatBroadcastMessage(subject.sender!, m),
);
const topBound = toMessage ? result.findIndex(r => r.msgId === toMessage.msgId) : -1;
const bottomBound = fromMessage ? result.findIndex(r => r.msgId === fromMessage.msgId) : -1;
return result.slice(bottomBound === -1 ? 0 : bottomBound + 1, topBound === -1 ? undefined : topBound);
}
async retrieveMessageHistoryByTime(
sender: string | null,
recipient: Uint256 | null,
fromTimestamp?: number,
toTimestamp?: number,
limit?: number,
): Promise<IMessage[]> {
const mailerAddress = this.getDefaultMailerAddress();
return this._retrieveMessageHistoryByTime(
mailerAddress,
{ type: BlockchainSourceType.DIRECT, sender, recipient },
fromTimestamp,
toTimestamp,
limit,
);
}
async retrieveMessageHistoryByBounds(
sender: string | null,
recipient: Uint256 | null,
fromMessage?: IMessage,
toMessage?: IMessage,
limit?: number,
): Promise<IMessage[]> {
const mailerAddress = this.getDefaultMailerAddress();
return this._retrieveMessageHistoryByBounds(
mailerAddress,
{ type: BlockchainSourceType.DIRECT, sender, recipient },
fromMessage,
toMessage,
limit,
);
}
async retrieveBroadcastHistoryByTime(
sender: string | null,
fromTimestamp?: number,
toTimestamp?: number,
limit?: number,
): Promise<IMessage[]> {
const broadcasterAddress = this.broadcasterContractAddress;
return this._retrieveMessageHistoryByTime(
broadcasterAddress,
{ type: BlockchainSourceType.BROADCAST, sender },
fromTimestamp,
toTimestamp,
limit,
);
}
async retrieveBroadcastHistoryByBounds(
sender: string | null,
fromMessage?: IMessage,
toMessage?: IMessage,
limit?: number,
): Promise<IMessage[]> {
const broadcasterAddress = this.broadcasterContractAddress;
return this._retrieveMessageHistoryByBounds(
broadcasterAddress,
{ type: BlockchainSourceType.BROADCAST, sender },
fromMessage,
toMessage,
limit,
);
}
private convertMsgIdToAddress(msgId: string) {
return `:${msgId}`;
}
async retrieveAndVerifyMessageContent(msg: IMessage): Promise<IMessageContent | IMessageCorruptedContent | null> {
const result = await this.retrieveMessageContentByMsgId(msg.msgId);
if (!result) {
return null;
}
if (result.corrupted) {
return result;
}
if (result.senderAddress.split(':')[1] !== msg.senderAddress.split(':')[1]) {
return {
msgId: msg.msgId,
corrupted: true,
chunks: [],
reason: MessageContentFailure.NON_INTEGRITY_PARTS,
};
}
return result;
}
async retrieveMessageContentByMsgId(msgId: string): Promise<IMessageContent | IMessageCorruptedContent | null> {
await core.ensureNekotonLoaded();
const fakeAddress = this.convertMsgIdToAddress(msgId);
let messages = await this.gql.queryContractMessages(fakeAddress, this.mailerContractAddress);
if (!messages.length) {
messages = await this.gql.queryContractMessages(fakeAddress, this.broadcasterContractAddress);
}
if (!messages.length) {
return null;
}
let decodedChunks: { msg: IEverscaleMessage; body: IEverscaleContentMessageBody }[];
try {
decodedChunks = messages.map((m: IEverscaleMessage) => ({
msg: m,
body: decodeContentMessageBody(m.body),
}));
} catch (err) {
return {
msgId,
corrupted: true,
chunks: messages.map((m: IEverscaleMessage) => ({ createdAt: m.created_at })),
reason: MessageContentFailure.NON_DECRYPTABLE,
};
}
const parts = decodedChunks[0].body.parts;
const sender = decodedChunks[0].body.sender;
if (!decodedChunks.every(t => t.body.parts === parts) || !decodedChunks.every(t => t.body.sender === sender)) {
return {
msgId,
corrupted: true,
chunks: decodedChunks.map(m => ({ createdAt: m.msg.created_at })),
reason: MessageContentFailure.NON_INTEGRITY_PARTS,
};
}
for (let idx = 0; idx < parts; idx++) {
if (!decodedChunks.find(d => d.body.partIdx === idx)) {
return {
msgId,
corrupted: true,
chunks: decodedChunks.map(m => ({ createdAt: m.msg.created_at })),
reason: MessageContentFailure.NOT_ALL_PARTS,
};
}
}
if (decodedChunks.length !== parts) {
return {
msgId,
corrupted: true,
chunks: decodedChunks.map(m => ({ createdAt: m.msg.created_at })),
reason: MessageContentFailure.DOUBLED_PARTS,
};
}
const sortedChunks = decodedChunks
.sort((a, b) => {
return a.body.partIdx - b.body.partIdx;
})
.map(m => m.body.content);
const contentSize = sortedChunks.reduce((p, c) => p + c.length, 0);
const buf = SmartBuffer.ofSize(contentSize);
for (const chunk of sortedChunks) {
buf.writeBytes(chunk);
}
return {
msgId,
corrupted: false,
storage: this.options.type === 'everscale-mainnet' ? 'everscale' : 'venom',
createdAt: Math.min(...decodedChunks.map(d => d.msg.created_at)),
senderAddress: sender,
parts,
content: buf.bytes,
};
}
private formatPushMessage(message: IEverscaleMessage): IMessage {
const body = decodePushMessageBody(message.body);
return {
isBroadcast: false,
msgId: body.msgId,
createdAt: message.created_at,
senderAddress: body.sender,
recipientAddress: this.addressToUint256(message.dst.startsWith(':') ? `0${message.dst}` : message.dst),
blockchain: this.options.type === 'everscale-mainnet' ? 'everscale' : 'venom',
key: body.key,
$$blockchainMetaDontUseThisField: message,
};
}
private formatBroadcastMessage(sender: string, message: IEverscaleMessage): IMessage {
const body = decodeBroadcastMessageBody(message.body);
return {
isBroadcast: true,
msgId: body.msgId,
createdAt: message.created_at,
senderAddress: message.dst,
recipientAddress: this.addressToUint256(message.dst.startsWith(':') ? `0${message.dst}` : message.dst),
blockchain: this.options.type === 'everscale-mainnet' ? 'everscale' : 'venom',
key: new Uint8Array(),
$$blockchainMetaDontUseThisField: message,
};
}
isAddressValid(address: string): boolean {
if (address.length !== 66) {
return false;
} else if (!address.includes(':')) {
return false;
}
const splitAddress = address.split(':');
if (splitAddress[0] !== '0') {
return false;
}
if (splitAddress[1].includes('_')) return false;
const regExp = new RegExp('^[^\\W]+$');
return regExp.test(splitAddress[1]);
}
private async queryMessagesListDescRaw(
gql: GqlSender,
contractAddress: string,
dst: string | null,
fromMessage: IEverscaleMessage | null,
// includeFromMessage: boolean,
toMessage: IEverscaleMessage | null,
// includeToMessage: boolean,
limit?: number,
nextPageAfterMessage?: IEverscaleMessage,
): Promise<IEverscaleMessage[]> {
let fromCursor: string | null = null;
if (fromMessage && !fromMessage.cursor) {
throw new Error('fromMessage.cursor is not defined');
}
if (toMessage && !toMessage.cursor) {
throw new Error('toMessage.cursor is not defined');
}
if (fromMessage) {
if (
nextPageAfterMessage &&
nextPageAfterMessage.created_lt &&
BigInt(nextPageAfterMessage.created_lt) < BigInt(fromMessage.created_lt)
) {
fromCursor = nextPageAfterMessage.cursor;
} else {
fromCursor = fromMessage.cursor;
}
} else {
if (nextPageAfterMessage && nextPageAfterMessage.created_lt) {
fromCursor = nextPageAfterMessage.cursor;
}
}
const result: IEverscaleMessage[] = await gql.queryMessages(`
query {
blockchain {
account(address:"${contractAddress}") {
messages(
msg_type: [ExtOut],
${dst ? `counterparties: ["${dst}"]` : ''}
${fromCursor ? `before: "${fromCursor}"` : ''}
last: ${Math.min(limit || this.MESSAGES_FETCH_LIMIT, this.MESSAGES_FETCH_LIMIT)}
) {
edges {
node {
body
msg_type
id
src
created_at
created_lt
dst
}
cursor
}
}
}
}
}
`);
let end = false;
const findTo = toMessage ? result.findIndex(x => x.id === toMessage.id) : -1;
if (findTo !== -1) {
result.splice(findTo);
end = true;
}
if (end || (limit && result.length === limit)) {
return result;
} else {
if (result.length === 0) {
return [];
} else {
const after = await this.queryMessagesListDescRaw(
gql,
contractAddress,
dst,
fromMessage,
// includeFromMessage,
toMessage,
// includeToMessage,
limit ? limit - result.length : undefined,
result[result.length - 1],
);
return result.concat(after);
}
}
}
// static async queryMessagesListDesc(
// gql: GqlSender,
// contractAddress: string,
// dst: string | null,
// fromMessage: IEverscaleMessage | null,
// // includeFromMessage: boolean,
// toMessage: IEverscaleMessage | null,
// // includeToMessage: boolean,
// limit?: number,
// ): Promise<IEverscaleMessage[]> {
// return this.queryMessagesListDescRaw(
// gql,
// contractAddress,
// dst,
// fromMessage ? fromMessage.$$meta : null,
// // includeFromMessage,
// toMessage ? toMessage.$$meta : null,
// // includeToMessage,
// limit,
// );
// }
// Query messages by interval sinceDate(excluded) - untilDate (excluded)
private async queryMessagesList(
contractAddress: string,
subject: ISourceSubject,
limit?: number,
filter?: {
fromDate?: number;
toDate?: number;
fromMessage?: IEverscaleMessage;
toMessage?: IEverscaleMessage;
},
nextPageAfterMessage?: IEverscaleMessage,
): Promise<IEverscaleMessage[]> {
const address =
subject.type === BlockchainSourceType.DIRECT
? subject.recipient
? uint256ToAddress(subject.recipient, true, true)
: null
: subject.sender
? subject.sender
: null;
return this.queryMessagesListDescRaw(
this.gql,
contractAddress,
address,
filter?.fromMessage || null,
filter?.toMessage || null,
limit,
);
// const createdAt: {
// gt?: number;
// lt?: number;
// } = {};
// const createdLt: {
// gt?: bigint;
// lt?: bigint;
// } = {};
// if (nextPageAfterMessage && nextPageAfterMessage.created_lt) {
// createdLt.lt = BigInt(nextPageAfterMessage.created_lt);
// }
// if (filter?.fromMessage) {
// createdLt.gt = BigInt(filter.fromMessage.created_lt);
// }
// if (filter?.toMessage) {
// const v = BigInt(filter.toMessage.created_lt);
// if (createdLt.lt === undefined || v < createdLt.lt) {
// createdLt.lt = v;
// }
// }
// if (filter?.fromDate !== undefined) {
// createdAt.gt = filter?.fromDate;
// }
// if (filter?.toDate !== undefined) {
// createdAt.lt = filter?.toDate;
// }
// // ${filter?.fromMessage ? `, created_lt: { gt: "${filter.fromMessage.created_lt}" }` : ``}
// // ${filter?.toMessage ? `, created_lt: { lt: "${filter.toMessage.created_lt}" }` : ``}
// // created_lt: { ${nextPageAfterMessage?.created_lt ? `lt: "${nextPageAfterMessage.created_lt}"` : ''} }
// // ${filter?.fromDate ? `, created_at: { gt: ${filter.fromDate} }` : ``}
// // ${filter?.toDate ? `, created_at: { lt: ${filter.toDate} }` : ``}
// const _at = createdAt.gt !== undefined || createdAt.lt !== undefined;
// const _lt = createdLt.gt !== undefined || createdLt.lt !== undefined;
// let result: IEverscaleMessage[];
// if (subject.type === BlockchainSourceType.DIRECT) {
// result = await this.gqlQueryMessages(
// `
// query {
// messages(
// filter: {
// msg_type: { eq: 2 },
// ${address ? `dst: { eq: "${address}" },` : ''}
// src: { eq: "${contractAddress}" },
// ${
// _at
// ? `created_at: { ${
// createdAt.lt !== undefined
// ? `lt: "${moment.unix(createdAt.lt).utc().toISOString()}", `
// : ''
// } ${
// createdAt.gt !== undefined
// ? `gt: "${moment.unix(createdAt.gt).utc().toISOString()}", `
// : ''
// } }, `
// : ''
// }
// ${
// _lt
// ? `created_lt: { ${
// createdLt.lt !== undefined ? `lt: "${'0x' + createdLt.lt.toString(16)}", ` : ''
// } ${
// createdLt.gt !== undefined ? `gt: "${'0x' + createdLt.gt.toString(16)}", ` : ''
// } }, `
// : ''
// }
// }
// orderBy: [{path: "created_at", direction: DESC}]
// limit: ${Math.min(limit || this.MESSAGES_FETCH_LIMIT, this.MESSAGES_FETCH_LIMIT)}
// ) {
// body
// id
// src
// created_at
// created_lt
// dst
// }
// }
// `,
// );
// } else {
// result = await this.gqlQueryMessages(
// `
// query {
// messages(
// filter: {
// msg_type: { eq: 2 },
// ${address ? `dst: { eq: "${address}" },` : ''}
// src: { eq: "${contractAddress}" },
// ${
// _at
// ? `created_at: { ${
// createdAt.lt !== undefined
// ? `lt: "${moment.unix(createdAt.lt).utc().toISOString()}", `
// : ''
// } ${
// createdAt.gt !== undefined
// ? `gt: "${moment.unix(createdAt.gt).utc().toISOString()}", `
// : ''
// } }, `
// : ''
// }
// ${
// _lt
// ? `created_lt: { ${
// createdLt.lt !== undefined
// ? `lt: "${'0x' + createdLt.lt.toString(16)}", `
// : ''
// } ${
// createdLt.gt !== undefined
// ? `gt: "${'0x' + createdLt.gt.toString(16)}", `
// : ''
// } }, `
// : ''
// }
// }
// orderBy: [{path: "created_at", direction: DESC}]
// limit: ${Math.min(limit || this.MESSAGES_FETCH_LIMIT, this.MESSAGES_FETCH_LIMIT)}
// ) {
// body
// id
// src
// created_at
// created_lt
// dst
// }
// }
// `,
// );
// }
// if (limit && result.length === limit) {
// return result;
// } else {
// if (result.length === 0) {
// return [];
// } else {
// const after = await this.queryMessagesList(
// contractAddress,
// subject,
// limit ? limit - result.length : undefined,
// filter,
// result[result.length - 1],
// );
// return result.concat(after);
// }
// }
}
async extractNativePublicKeyFromAddress(addressStr: string): Promise<Uint8Array | null> {
const nt = core.nekoton;
await core.ensureNekotonLoaded();
const address = new Address(addressStr);
const boc = await this.ever.getFullContractState({ address });
if (!boc.state) {
return null;
}
try {
const pk = nt.extractPublicKey(boc.state.boc);
return pk ? SmartBuffer.ofHexString(pk).bytes : null;
} catch (err) {
return null;
}
}
async decodeNativeKey(
senderPublicKey: Uint8Array,
recipientPublicKey: Uint8Array,
key: Uint8Array,
): Promise<Uint8Array> {
try {
const { encData, nonce } = unpackSymmetricalyEncryptedData(key);
const decryptedText = await this.ever.decryptData({
algorithm: 'ChaCha20Poly1305',
data: new SmartBuffer(encData).toBase64String(),
nonce: new SmartBuffer(nonce).toBase64String(),
recipientPublicKey: new SmartBuffer(recipientPublicKey).toHexString(),
sourcePublicKey: new SmartBuffer(senderPublicKey).toHexString(),
});
if (decryptedText) {
return SmartBuffer.ofBase64String(decryptedText).bytes;
} else {
throw new Error('Error decrypting message text');
}
} catch (e) {
throw e;
}
}
async getExtraEncryptionStrategiesFromAddress(address: string): Promise<IExtraEncryptionStrateryEntry[]> {
const native = await this.extractNativePublicKeyFromAddress(address);
if (native) {
return [
{
ylide: false,
blockchain: this.options.type === 'everscale-mainnet' ? 'everscale' : 'venom',
address,
type: this.options.type === 'everscale-mainnet' ? 'everscale-native' : 'venom-native',
data: {
nativePublicKey: native,
},
},
];
} else {
return [];
}
}
getSupportedExtraEncryptionStrategies(): string[] {
return [this.options.type === 'everscale-mainnet' ? 'everscale-native' : 'venom-native'];
}
async prepareExtraEncryptionStrategyBulk(
entries: IExtraEncryptionStrateryEntry[],
): Promise<IExtraEncryptionStrateryBulk> {
await core.ensureNekotonLoaded();
const ephemeralSecret = generate_ephemeral();
const ephemeralPublic = get_public_key(ephemeralSecret);
return {
addedPublicKey: {
key: PublicKey.fromHexString(PublicKeyType.EVERSCALE_NATIVE, ephemeralPublic),
},
blockchain: this.options.type === 'everscale-mainnet' ? 'everscale' : 'venom',
type: this.options.type === 'everscale-mainnet' ? 'everscale-native' : 'venom-native',
data: {
nativeEphemeralKeySecret: ephemeralSecret,
},
};
}
async executeExtraEncryptionStrategy(
entries: IExtraEncryptionStrateryEntry[],
bulk: IExtraEncryptionStrateryBulk,
addedPublicKeyIndex: number | null,
messageKey: Uint8Array,
): Promise<MessageKey[]> {
const nativeSenderPrivateKey = SmartBuffer.ofHexString(bulk.data.nativeEphemeralKeySecret);
return entries.map(entry => {
const recipientNativePublicKey = new SmartBuffer(entry.data.nativePublicKey);
const nonce = new SmartBuffer(nacl.randomBytes(12));
const encryptedKey = SmartBuffer.ofHexString(
encrypt(
nativeSenderPrivateKey.toHexString(),
recipientNativePublicKey.toHexString(),
new SmartBuffer(messageKey).toHexString(),
nonce.toHexString(),
),
);
const packedKey = packSymmetricalyEncryptedData(encryptedKey.bytes, nonce.bytes);
return new MessageKey(addedPublicKeyIndex!, packedKey);
});
}
addressToUint256(address: string): Uint256 {
return hexToUint256(address.split(':')[1].toLowerCase());
}
compareMessagesTime(a: IMessage, b: IMessage): number {
if (a.createdAt === b.createdAt) {
return 0;
} else {
return a.createdAt - b.createdAt;
}
}
}
// https://gql-testnet.venom.foundation/graphql
export const everscaleBlockchainFactory: BlockchainControllerFactory = {
create: async (options?: any) =>
new EverscaleBlockchainController(Object.assign({ type: 'everscale-mainnet' }, options || {})),
blockchain: 'everscale',
blockchainGroup: 'everscale',
};
export const venomBlockchainFactory: BlockchainControllerFactory = {
create: async (options?: any) =>
new EverscaleBlockchainController(Object.assign({ type: 'venom-testnet' }, options || {})),
blockchain: 'venom',
blockchainGroup: 'venom',
};