feat: Tonhub Connector
parent
0e47abc7eb
commit
12d0837df9
13
package.json
13
package.json
|
@ -14,5 +14,16 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^4.6.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/bn.js": "^5.1.0",
|
||||
"axios": "^0.27.0",
|
||||
"bn.js": "^5.2.0",
|
||||
"fp-ts": "^2.12.0",
|
||||
"io-ts": "^2.2.16",
|
||||
"teslabot": "^1.5.0",
|
||||
"ton": "9.8.0",
|
||||
"ton-contracts": "^3.0.0",
|
||||
"ton-crypto": "^3.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,554 @@
|
|||
import axios, { AxiosAdapter } from "axios";
|
||||
import { getSecureRandomBytes, keyPairFromSeed } from "ton-crypto";
|
||||
import { backoff } from "../utils/backoff";
|
||||
import { toUrlSafe } from "../utils/toURLsafe";
|
||||
import * as t from 'io-ts';
|
||||
import { delay } from "teslabot";
|
||||
import { Cell, Address, beginCell, CommentMessage, safeSign, contractAddress, safeSignVerify } from 'ton';
|
||||
import BN from 'bn.js';
|
||||
import { WalletV4Source } from 'ton-contracts';
|
||||
|
||||
const sessionStateCodec = t.union([
|
||||
t.type({
|
||||
state: t.literal('not_found')
|
||||
}),
|
||||
t.type({
|
||||
state: t.literal('initing'),
|
||||
name: t.string,
|
||||
url: t.string,
|
||||
testnet: t.boolean,
|
||||
created: t.number,
|
||||
updated: t.number,
|
||||
revoked: t.boolean
|
||||
}),
|
||||
t.type({
|
||||
state: t.literal('ready'),
|
||||
name: t.string,
|
||||
url: t.string,
|
||||
wallet: t.type({
|
||||
address: t.string,
|
||||
endpoint: t.string,
|
||||
walletConfig: t.string,
|
||||
walletType: t.string,
|
||||
walletSig: t.string,
|
||||
appPublicKey: t.string
|
||||
}),
|
||||
testnet: t.boolean,
|
||||
created: t.number,
|
||||
updated: t.number,
|
||||
revoked: t.boolean
|
||||
})
|
||||
]);
|
||||
|
||||
const jobStateCodec = t.union([t.type({
|
||||
state: t.union([t.literal('submitted'), t.literal('expired'), t.literal('rejected')]),
|
||||
job: t.string,
|
||||
created: t.number,
|
||||
updated: t.number,
|
||||
now: t.number
|
||||
}), t.type({
|
||||
state: t.literal('completed'),
|
||||
job: t.string,
|
||||
created: t.number,
|
||||
updated: t.number,
|
||||
result: t.string,
|
||||
now: t.number
|
||||
}), t.type({
|
||||
state: t.literal('empty'),
|
||||
now: t.number
|
||||
})]);
|
||||
|
||||
export type TonhubWalletConfig = {
|
||||
address: string,
|
||||
endpoint: string,
|
||||
walletType: string,
|
||||
walletConfig: string,
|
||||
walletSig: string,
|
||||
appPublicKey: string
|
||||
}
|
||||
|
||||
export type TonhubCreatedSession = {
|
||||
id: string
|
||||
seed: string,
|
||||
link: string
|
||||
};
|
||||
|
||||
export type TonhubSessionStateRevoked = {
|
||||
state: 'revoked'
|
||||
};
|
||||
|
||||
export type TonhubSessionStateExpired = {
|
||||
state: 'expired'
|
||||
}
|
||||
|
||||
export type TonhubSessionStateReady = {
|
||||
state: 'ready',
|
||||
name: string,
|
||||
url: string,
|
||||
created: number,
|
||||
updated: number,
|
||||
wallet: TonhubWalletConfig
|
||||
}
|
||||
|
||||
export type TonhubSessionStateIniting = {
|
||||
state: 'initing',
|
||||
name: string,
|
||||
url: string,
|
||||
created: number,
|
||||
updated: number
|
||||
}
|
||||
|
||||
export type TonhubSessionState = TonhubSessionStateIniting | TonhubSessionStateRevoked | TonhubSessionStateReady;
|
||||
export type TonhubSessionAwaited = TonhubSessionStateRevoked | TonhubSessionStateReady | TonhubSessionStateExpired;
|
||||
|
||||
export type TonhubTransactionRequest = {
|
||||
seed: string,
|
||||
appPublicKey: string,
|
||||
to: string,
|
||||
value: string,
|
||||
timeout: number,
|
||||
stateInit?: string | null | undefined,
|
||||
text?: string | null | undefined,
|
||||
payload?: string | null | undefined
|
||||
};
|
||||
|
||||
export type TonhubTransactionResponse = {
|
||||
type: 'success',
|
||||
response: string
|
||||
} | {
|
||||
type: 'rejected'
|
||||
} | {
|
||||
type: 'expired'
|
||||
} | {
|
||||
type: 'invalid_session'
|
||||
};
|
||||
|
||||
export type TonhubSignRequest = {
|
||||
seed: string,
|
||||
appPublicKey: string,
|
||||
timeout: number,
|
||||
text?: string | null | undefined,
|
||||
payload?: string | null | undefined
|
||||
}
|
||||
|
||||
export type TonhubSignResponse = {
|
||||
type: 'success',
|
||||
signature: string
|
||||
} | {
|
||||
type: 'rejected'
|
||||
} | {
|
||||
type: 'expired'
|
||||
} | {
|
||||
type: 'invalid_session'
|
||||
};
|
||||
|
||||
function idFromSeed(seed: string) {
|
||||
let keyPair = keyPairFromSeed(Buffer.from(seed, 'base64'));
|
||||
return toUrlSafe(keyPair.publicKey.toString('base64'));
|
||||
}
|
||||
|
||||
export class TonhubConnector {
|
||||
|
||||
static extractPublicKey(config: {
|
||||
walletType: string,
|
||||
walletConfig: string
|
||||
}) {
|
||||
// Extract public key and address
|
||||
let publicKey: Buffer;
|
||||
let restoredAddress: Address;
|
||||
if (config.walletType === 'org.ton.wallets.v4') {
|
||||
let source = WalletV4Source.restore(config.walletConfig);
|
||||
restoredAddress = contractAddress(source);
|
||||
publicKey = source.publicKey;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Public key
|
||||
return { publicKey, address: restoredAddress };
|
||||
}
|
||||
|
||||
static verifyWalletConfig(session: string, config: TonhubWalletConfig) {
|
||||
|
||||
// Check address
|
||||
const address = Address.parseFriendly(config.address).address;
|
||||
|
||||
// Extract public key and address
|
||||
let extracted = TonhubConnector.extractPublicKey(config);
|
||||
if (!extracted) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check address
|
||||
if (!extracted.address.equals(address)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let publicKey: Buffer = extracted.publicKey;
|
||||
|
||||
// Check signature
|
||||
let toSign = beginCell()
|
||||
.storeCoins(0)
|
||||
.storeBuffer(Buffer.from(session, 'base64'))
|
||||
.storeAddress(address)
|
||||
// Endpoint
|
||||
.storeBit(1)
|
||||
.storeRef(beginCell()
|
||||
.storeBuffer(Buffer.from(config.endpoint))
|
||||
.endCell())
|
||||
// App Public Key
|
||||
.storeRef(beginCell()
|
||||
.storeBuffer(Buffer.from(config.appPublicKey, 'base64'))
|
||||
.endCell())
|
||||
.endCell();
|
||||
|
||||
// Sign
|
||||
return safeSignVerify(toSign, Buffer.from(config.walletSig, 'base64'), publicKey);
|
||||
}
|
||||
|
||||
static verifySignatureResponse(args: {
|
||||
signature: string,
|
||||
text?: string | null | undefined,
|
||||
payload?: string | null | undefined,
|
||||
config: TonhubWalletConfig
|
||||
}) {
|
||||
// Check address
|
||||
const address = Address.parseFriendly(args.config.address).address;
|
||||
|
||||
// Extract public key and address
|
||||
let extracted = TonhubConnector.extractPublicKey(args.config);
|
||||
if (!extracted) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check address
|
||||
if (!extracted.address.equals(address)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let publicKey: Buffer = extracted.publicKey;
|
||||
|
||||
// Package
|
||||
const textCell = new Cell();
|
||||
const payloadCell = new Cell();
|
||||
if (typeof args.text === 'string') {
|
||||
new CommentMessage(args.text).writeTo(textCell);
|
||||
}
|
||||
|
||||
// Check signature
|
||||
const data = beginCell()
|
||||
.storeRef(textCell)
|
||||
.storeRef(payloadCell)
|
||||
.endCell();
|
||||
const signed = safeSignVerify(data, Buffer.from(args.signature, 'base64'), publicKey);
|
||||
|
||||
return signed;
|
||||
}
|
||||
|
||||
readonly testnet: boolean;
|
||||
readonly adapter: AxiosAdapter | null;
|
||||
|
||||
constructor(args?: { testnet?: boolean, adapter?: AxiosAdapter }) {
|
||||
let testnet = false;
|
||||
let adapter: AxiosAdapter | null = null;
|
||||
if (args) {
|
||||
if (typeof args.testnet === 'boolean') {
|
||||
testnet = args.testnet;
|
||||
}
|
||||
}
|
||||
this.testnet = testnet;
|
||||
this.adapter = adapter;
|
||||
}
|
||||
|
||||
createNewSession = async (args: { name: string, url: string }): Promise<TonhubCreatedSession> => {
|
||||
|
||||
// Generate new key
|
||||
let seed = await getSecureRandomBytes(32);
|
||||
let keyPair = keyPairFromSeed(seed);
|
||||
let sessionId = toUrlSafe(keyPair.publicKey.toString('base64'));
|
||||
|
||||
// Request new session
|
||||
await backoff(async () => {
|
||||
let session = await axios.post('https://connect.tonhubapi.com/connect/init', {
|
||||
key: sessionId,
|
||||
testnet: this.testnet,
|
||||
name: args.name,
|
||||
url: args.url
|
||||
}, { timeout: 5000, adapter: this.adapter ? this.adapter : undefined });
|
||||
if (!session.data.ok) {
|
||||
throw Error('Unable to create state');
|
||||
}
|
||||
});
|
||||
|
||||
// Return session
|
||||
return {
|
||||
id: sessionId,
|
||||
seed: seed.toString('base64'),
|
||||
link: (this.testnet ? 'ton-test://connect/' : 'ton://connect/') + sessionId + '?endpoint=connect.tonhubapi.com'
|
||||
};
|
||||
}
|
||||
|
||||
getSessionState = async (sessionId: string): Promise<TonhubSessionState> => {
|
||||
return await backoff(async () => {
|
||||
let ex = (await axios.get('https://connect.tonhubapi.com/connect/' + sessionId, { adapter: this.adapter ? this.adapter : undefined, timeout: 5000 })).data;
|
||||
if (!sessionStateCodec.is(ex)) {
|
||||
throw Error('Invalid response from server');
|
||||
}
|
||||
if (ex.state === 'initing') {
|
||||
if (ex.testnet !== this.testnet) {
|
||||
return { state: 'revoked' };
|
||||
}
|
||||
return {
|
||||
state: 'initing',
|
||||
name: ex.name,
|
||||
url: ex.url,
|
||||
created: ex.created,
|
||||
updated: ex.updated
|
||||
};
|
||||
}
|
||||
if (ex.state === 'ready') {
|
||||
if (ex.revoked) {
|
||||
return { state: 'revoked' };
|
||||
}
|
||||
if (ex.testnet !== this.testnet) {
|
||||
return { state: 'revoked' };
|
||||
}
|
||||
if (!TonhubConnector.verifyWalletConfig(sessionId, ex.wallet)) {
|
||||
throw Error('Integrity check failed');
|
||||
}
|
||||
|
||||
return {
|
||||
state: 'ready',
|
||||
name: ex.name,
|
||||
url: ex.url,
|
||||
created: ex.created,
|
||||
updated: ex.updated,
|
||||
wallet: {
|
||||
address: ex.wallet.address,
|
||||
endpoint: ex.wallet.endpoint,
|
||||
walletType: ex.wallet.walletType,
|
||||
walletConfig: ex.wallet.walletConfig,
|
||||
walletSig: ex.wallet.walletSig,
|
||||
appPublicKey: ex.wallet.appPublicKey
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return { state: 'revoked' };
|
||||
});
|
||||
}
|
||||
|
||||
awaitSessionReady = async (sessionId: string, timeout: number): Promise<TonhubSessionAwaited> => {
|
||||
let expires = Date.now() + timeout;
|
||||
let res: TonhubSessionStateReady | TonhubSessionStateExpired | TonhubSessionStateRevoked = await backoff(async () => {
|
||||
while (Date.now() < expires) {
|
||||
let existing = await this.getSessionState(sessionId);
|
||||
if (existing.state !== 'initing') {
|
||||
if (existing.state === 'ready') {
|
||||
return existing;
|
||||
} else if (existing.state === 'revoked') {
|
||||
return existing;
|
||||
}
|
||||
}
|
||||
await delay(1000);
|
||||
}
|
||||
return { state: 'expired' };
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
requestTransaction = async (request: TonhubTransactionRequest): Promise<TonhubTransactionResponse> => {
|
||||
const sessionId = idFromSeed(request.seed);
|
||||
|
||||
// Check session
|
||||
let session = await backoff(() => this.getSessionState(sessionId));
|
||||
if (session.state !== 'ready') {
|
||||
return { type: 'invalid_session' };
|
||||
}
|
||||
if (session.wallet.appPublicKey !== request.appPublicKey) {
|
||||
return { type: 'invalid_session' };
|
||||
}
|
||||
|
||||
// Parse address
|
||||
let address = Address.parseFriendly(request.to).address;
|
||||
|
||||
// Value
|
||||
let value = new BN(request.value, 10);
|
||||
|
||||
// Parse data
|
||||
let data: Cell | null = null;
|
||||
if (typeof request.payload === 'string') {
|
||||
data = Cell.fromBoc(Buffer.from(request.payload, 'base64'))[0];
|
||||
}
|
||||
|
||||
// StateInit
|
||||
let stateInit: Cell | null = null;
|
||||
if (typeof request.stateInit === 'string') {
|
||||
data = Cell.fromBoc(Buffer.from(request.stateInit, 'base64'))[0];
|
||||
}
|
||||
|
||||
// Comment
|
||||
let comment: string = '';
|
||||
if (typeof request.text === 'string') {
|
||||
comment = request.text;
|
||||
}
|
||||
|
||||
// Prepare cell
|
||||
let expires = Math.floor((Date.now() + request.timeout) / 1000);
|
||||
let commentCell = new Cell();
|
||||
new CommentMessage(comment).writeTo(commentCell);
|
||||
const job = beginCell()
|
||||
.storeBuffer(Buffer.from(session.wallet.appPublicKey, 'base64'))
|
||||
.storeUint(expires, 32)
|
||||
.storeCoins(0)
|
||||
.storeRef(beginCell()
|
||||
.storeAddress(address)
|
||||
.storeCoins(value)
|
||||
.storeRef(commentCell)
|
||||
.storeRefMaybe(data ? data : null)
|
||||
.storeRefMaybe(stateInit ? stateInit : null)
|
||||
.endCell())
|
||||
.endCell()
|
||||
|
||||
// Sign
|
||||
let keypair = keyPairFromSeed(Buffer.from(request.seed, 'base64'));
|
||||
let signature = safeSign(job, keypair.secretKey);
|
||||
|
||||
// Create package
|
||||
let pkg = beginCell()
|
||||
.storeBuffer(signature)
|
||||
.storeBuffer(keypair.publicKey)
|
||||
.storeRef(job)
|
||||
.endCell();
|
||||
let boc = pkg.toBoc({ idx: false }).toString('base64');
|
||||
|
||||
// Post command
|
||||
await backoff(() => axios.post('https://connect.tonhubapi.com/connect/command', { job: boc }, { adapter: this.adapter ? this.adapter : undefined, timeout: 5000 }));
|
||||
|
||||
// Await result
|
||||
let result = await this._awaitJobState(request.appPublicKey, boc);
|
||||
if (result.type === 'completed') {
|
||||
return { type: 'success', response: result.result };
|
||||
} else if (result.type === 'rejected') {
|
||||
return { type: 'rejected' };
|
||||
}
|
||||
return { type: 'expired' };
|
||||
}
|
||||
|
||||
requestSign = async (request: TonhubSignRequest): Promise<TonhubSignResponse> => {
|
||||
|
||||
const sessionId = idFromSeed(request.seed);
|
||||
|
||||
// Check session
|
||||
let session = await backoff(() => this.getSessionState(sessionId));
|
||||
if (session.state !== 'ready') {
|
||||
return { type: 'invalid_session' };
|
||||
}
|
||||
if (session.wallet.appPublicKey !== request.appPublicKey) {
|
||||
return { type: 'invalid_session' };
|
||||
}
|
||||
|
||||
// Parse data
|
||||
let data: Cell = new Cell();
|
||||
if (typeof request.payload === 'string') {
|
||||
data = Cell.fromBoc(Buffer.from(request.payload, 'base64'))[0];
|
||||
}
|
||||
|
||||
// Comment
|
||||
let comment: string = '';
|
||||
if (typeof request.text === 'string') {
|
||||
comment = request.text;
|
||||
}
|
||||
|
||||
// Prepare cell
|
||||
let expires = Math.floor((Date.now() + request.timeout) / 1000);
|
||||
let commentCell = new Cell();
|
||||
new CommentMessage(comment).writeTo(commentCell);
|
||||
const job = beginCell()
|
||||
.storeBuffer(Buffer.from(session.wallet.appPublicKey, 'base64'))
|
||||
.storeUint(expires, 32)
|
||||
.storeCoins(1)
|
||||
.storeRef(beginCell()
|
||||
.storeRef(commentCell)
|
||||
.storeRef(data)
|
||||
.endCell())
|
||||
.endCell();
|
||||
|
||||
// Sign
|
||||
let keypair = keyPairFromSeed(Buffer.from(request.seed, 'base64'));
|
||||
let signature = safeSign(job, keypair.secretKey);
|
||||
|
||||
// Create package
|
||||
let pkg = beginCell()
|
||||
.storeBuffer(signature)
|
||||
.storeBuffer(keypair.publicKey)
|
||||
.storeRef(job)
|
||||
.endCell();
|
||||
let boc = pkg.toBoc({ idx: false }).toString('base64');
|
||||
|
||||
// Post command
|
||||
await backoff(() => axios.post('https://connect.tonhubapi.com/connect/command', { job: boc }, { adapter: this.adapter ? this.adapter : undefined, timeout: 5000 }));
|
||||
|
||||
// Await result
|
||||
let result = await this._awaitJobState(request.appPublicKey, boc);
|
||||
if (result.type === 'completed') {
|
||||
const cellRes = Cell.fromBoc(Buffer.from(result.result, 'base64'))[0];
|
||||
let slice = cellRes.beginParse();
|
||||
const resSignature = slice.readBuffer(64);
|
||||
let correct = TonhubConnector.verifySignatureResponse({ signature: resSignature.toString('base64'), config: session.wallet });
|
||||
if (correct) {
|
||||
return { type: 'success', signature: resSignature.toString('base64') };
|
||||
} else {
|
||||
return { type: 'rejected' };
|
||||
}
|
||||
} else if (result.type === 'rejected') {
|
||||
return { type: 'rejected' };
|
||||
}
|
||||
return { type: 'expired' };
|
||||
}
|
||||
|
||||
private _awaitJobState = async (appPublicKey: string, boc: string): Promise<{ type: 'completed', result: string } | { type: 'rejected' | 'expired' }> => {
|
||||
return await backoff(async (): Promise<{ type: 'completed', result: string } | { type: 'rejected' | 'expired' }> => {
|
||||
while (true) {
|
||||
let state = await this._getJobState(appPublicKey, boc);
|
||||
if (state.type === 'expired') {
|
||||
return { type: 'expired' };
|
||||
}
|
||||
if (state.type === 'completed') {
|
||||
return { type: 'completed', result: state.result };
|
||||
}
|
||||
if (state.type === 'rejected') {
|
||||
return { type: 'rejected' };
|
||||
}
|
||||
await delay(1000);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _getJobState = async (appPublicKey: string, boc: string): Promise<{ type: 'expired' | 'rejected' | 'submitted' } | { type: 'completed', result: string }> => {
|
||||
let appk = toUrlSafe(appPublicKey);
|
||||
let res = (await axios.get('https://connect.tonhubapi.com/connect/command/' + appk, { adapter: this.adapter ? this.adapter : undefined, timeout: 5000 })).data;
|
||||
if (!jobStateCodec.is(res)) {
|
||||
throw Error('Invalid response from server');
|
||||
}
|
||||
if (res.state === 'empty') {
|
||||
return { type: 'expired' };
|
||||
}
|
||||
if (res.job !== boc) {
|
||||
return { type: 'rejected' };
|
||||
}
|
||||
if (res.state === 'expired') {
|
||||
return { type: 'expired' };
|
||||
}
|
||||
if (res.state === 'submitted') {
|
||||
return { type: 'submitted' };
|
||||
}
|
||||
if (res.state === 'rejected') {
|
||||
return { type: 'rejected' };
|
||||
}
|
||||
if (res.state === 'completed') {
|
||||
return { type: 'completed', result: res.result };
|
||||
}
|
||||
throw Error('Invalid response from server');
|
||||
};
|
||||
}
|
15
src/index.ts
15
src/index.ts
|
@ -0,0 +1,15 @@
|
|||
export {
|
||||
TonhubCreatedSession,
|
||||
TonhubSessionStateRevoked,
|
||||
TonhubSessionStateExpired,
|
||||
TonhubSessionStateReady,
|
||||
TonhubSessionStateIniting,
|
||||
TonhubSessionState,
|
||||
TonhubSessionAwaited,
|
||||
TonhubConnector,
|
||||
TonhubTransactionRequest,
|
||||
TonhubTransactionResponse,
|
||||
TonhubSignRequest,
|
||||
TonhubSignResponse,
|
||||
TonhubWalletConfig
|
||||
} from './connector/TonhubConnector';
|
|
@ -0,0 +1,6 @@
|
|||
import { createBackoff } from "teslabot";
|
||||
|
||||
export const backoff = createBackoff({
|
||||
maxFailureCount: 5,
|
||||
onError: (e, i) => i > 3 && console.warn(e)
|
||||
});
|
|
@ -0,0 +1,12 @@
|
|||
export function toUrlSafe(src: string) {
|
||||
while (src.indexOf('/') >= 0) {
|
||||
src = src.replace('/', '_');
|
||||
}
|
||||
while (src.indexOf('+') >= 0) {
|
||||
src = src.replace('+', '-');
|
||||
}
|
||||
while (src.indexOf('=') >= 0) {
|
||||
src = src.replace('=', '');
|
||||
}
|
||||
return src;
|
||||
}
|
209
yarn.lock
209
yarn.lock
|
@ -2,6 +2,215 @@
|
|||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@openland/patterns@^0.0.2":
|
||||
version "0.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@openland/patterns/-/patterns-0.0.2.tgz#ada0417c4e72aeeb461bf9d8759ecd9ca1ebf51d"
|
||||
integrity sha512-ttgJELwtG8cIER1JeWjkIMg3e+MlAL6n4wFPSNPoBCl+5t3G/HIz9kolvxBmN5/3RXEgh68NnePzGiUBaUHmRQ==
|
||||
|
||||
"@scarf/scarf@^1.1.1":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@scarf/scarf/-/scarf-1.1.1.tgz#d8b9f20037b3a37dbf8dcdc4b3b72f9285bfce35"
|
||||
integrity sha512-VGbKDbk1RFIaSmdVb0cNjjWJoRWRI/Weo23AjRCC2nryO0iAS8pzsToJfPVPtVs74WHw4L1UTADNdIYRLkirZQ==
|
||||
|
||||
"@types/bn.js@^5.1.0":
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.0.tgz#32c5d271503a12653c62cf4d2b45e6eab8cebc68"
|
||||
integrity sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/node@*":
|
||||
version "17.0.27"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.27.tgz#f4df3981ae8268c066e8f49995639f855469081e"
|
||||
integrity sha512-4/Ke7bbWOasuT3kceBZFGakP1dYN2XFd8v2l9bqF2LNWrmeU07JLpp56aEeG6+Q3olqO5TvXpW0yaiYnZJ5CXg==
|
||||
|
||||
asynckit@^0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
|
||||
integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
|
||||
|
||||
axios@^0.25.0:
|
||||
version "0.25.0"
|
||||
resolved "https://registry.yarnpkg.com/axios/-/axios-0.25.0.tgz#349cfbb31331a9b4453190791760a8d35b093e0a"
|
||||
integrity sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==
|
||||
dependencies:
|
||||
follow-redirects "^1.14.7"
|
||||
|
||||
axios@^0.27.0:
|
||||
version "0.27.0"
|
||||
resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.0.tgz#973ca980dee9077410189340390ea77964af6b29"
|
||||
integrity sha512-XV/WrPxXfzgZ8j4lcB5i6LyaXmi90yetmV/Fem0kmglGx+mpY06CiweL3YxU6wOTNLmqLUePW4G8h45nGZ/+pA==
|
||||
dependencies:
|
||||
follow-redirects "^1.14.9"
|
||||
form-data "^4.0.0"
|
||||
|
||||
bn.js@4.11.6:
|
||||
version "4.11.6"
|
||||
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215"
|
||||
integrity sha1-UzRK2xRhehP26N0s4okF0cC6MhU=
|
||||
|
||||
bn.js@5.2.0, bn.js@^5.2.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002"
|
||||
integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==
|
||||
|
||||
combined-stream@^1.0.8:
|
||||
version "1.0.8"
|
||||
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
|
||||
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
|
||||
dependencies:
|
||||
delayed-stream "~1.0.0"
|
||||
|
||||
dataloader@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-2.1.0.tgz#c69c538235e85e7ac6c6c444bae8ecabf5de9df7"
|
||||
integrity sha512-qTcEYLen3r7ojZNgVUaRggOI+KM7jrKxXeSHhogh/TWxYMeONEMqY+hmkobiYQozsGIyg9OYVzO4ZIfoB4I0pQ==
|
||||
|
||||
delayed-stream@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
|
||||
integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
|
||||
|
||||
ethjs-unit@0.1.6:
|
||||
version "0.1.6"
|
||||
resolved "https://registry.yarnpkg.com/ethjs-unit/-/ethjs-unit-0.1.6.tgz#c665921e476e87bce2a9d588a6fe0405b2c41699"
|
||||
integrity sha1-xmWSHkduh7ziqdWIpv4EBbLEFpk=
|
||||
dependencies:
|
||||
bn.js "4.11.6"
|
||||
number-to-bn "1.7.0"
|
||||
|
||||
follow-redirects@^1.14.7, follow-redirects@^1.14.9:
|
||||
version "1.14.9"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.9.tgz#dd4ea157de7bfaf9ea9b3fbd85aa16951f78d8d7"
|
||||
integrity sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==
|
||||
|
||||
form-data@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
|
||||
integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
|
||||
dependencies:
|
||||
asynckit "^0.4.0"
|
||||
combined-stream "^1.0.8"
|
||||
mime-types "^2.1.12"
|
||||
|
||||
fp-ts@^2.11.1, fp-ts@^2.12.0:
|
||||
version "2.12.0"
|
||||
resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-2.12.0.tgz#6a0c9e99f69576aaa5a6c94c1284ba977f5569b7"
|
||||
integrity sha512-ZIMpTxc4Vadj3gs3Geg4tohB7eSkC125TA7ZH2ddEcREmjpjpbq6wUxWQmFFfAOCRWEkljh3BPLZGBVj9HB9Xw==
|
||||
|
||||
io-ts-reporters@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/io-ts-reporters/-/io-ts-reporters-2.0.1.tgz#0154545aefd115f0f175a41743ed7327d19b1c34"
|
||||
integrity sha512-RVpLstYBsmTGgCW9wJ5KVyN/eRnRUDp87Flt4D1O3aJ7oAnd8csq8aXuu7ZeNK8qEDKmjUl9oUuzfwikaNAMKQ==
|
||||
dependencies:
|
||||
"@scarf/scarf" "^1.1.1"
|
||||
|
||||
io-ts@^2.2.16:
|
||||
version "2.2.16"
|
||||
resolved "https://registry.yarnpkg.com/io-ts/-/io-ts-2.2.16.tgz#597dffa03db1913fc318c9c6df6931cb4ed808b2"
|
||||
integrity sha512-y5TTSa6VP6le0hhmIyN0dqEXkrZeJLeC5KApJq6VLci3UEKF80lZ+KuoUs02RhBxNWlrqSNxzfI7otLX1Euv8Q==
|
||||
|
||||
is-hex-prefixed@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz#7d8d37e6ad77e5d127148913c573e082d777f554"
|
||||
integrity sha1-fY035q135dEnFIkTxXPggtd39VQ=
|
||||
|
||||
jssha@3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/jssha/-/jssha-3.2.0.tgz#88ec50b866dd1411deaddbe6b3e3692e4c710f16"
|
||||
integrity sha512-QuruyBENDWdN4tZwJbQq7/eAK85FqrI4oDbXjy5IBhYD+2pTJyBUWZe8ctWaCkrV0gy6AaelgOZZBMeswEa/6Q==
|
||||
|
||||
mime-db@1.52.0:
|
||||
version "1.52.0"
|
||||
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
|
||||
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
|
||||
|
||||
mime-types@^2.1.12:
|
||||
version "2.1.35"
|
||||
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
|
||||
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
|
||||
dependencies:
|
||||
mime-db "1.52.0"
|
||||
|
||||
number-to-bn@1.7.0:
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/number-to-bn/-/number-to-bn-1.7.0.tgz#bb3623592f7e5f9e0030b1977bd41a0c53fe1ea0"
|
||||
integrity sha1-uzYjWS9+X54AMLGXe9QaDFP+HqA=
|
||||
dependencies:
|
||||
bn.js "4.11.6"
|
||||
strip-hex-prefix "1.0.0"
|
||||
|
||||
strip-hex-prefix@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz#0c5f155fef1151373377de9dbb588da05500e36f"
|
||||
integrity sha1-DF8VX+8RUTczd96du1iNoFUA428=
|
||||
dependencies:
|
||||
is-hex-prefixed "1.0.0"
|
||||
|
||||
symbol.inspect@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/symbol.inspect/-/symbol.inspect-1.0.1.tgz#e13125b8038c4996eb0dfa1567332ad4dcd0763f"
|
||||
integrity sha512-YQSL4duoHmLhsTD1Pw8RW6TZ5MaTX5rXJnqacJottr2P2LZBF/Yvrc3ku4NUpMOm8aM0KOCqM+UAkMA5HWQCzQ==
|
||||
|
||||
teslabot@^1.3.0, teslabot@^1.5.0:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/teslabot/-/teslabot-1.5.0.tgz#70f544516699ca5f696d8ae94f3d12cd495d5cd6"
|
||||
integrity sha512-e2MmELhCgrgZEGo7PQu/6bmYG36IDH+YrBI1iGm6jovXkeDIGa3pZ2WSqRjzkuw2vt1EqfkZoV5GpXgqL8QJVg==
|
||||
|
||||
ton-contracts@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ton-contracts/-/ton-contracts-3.0.0.tgz#f3aa68e85fa963836d3aa0b67eae9196f1e67b94"
|
||||
integrity sha512-62i/fyFQSTEV9PUFiFd74L9CSWLRBxbuDp7nPzNYTaWBFR+ijTqKand+bfEZLIkNOUoY4jlrEogO5ZnNhYq1Zw==
|
||||
dependencies:
|
||||
"@openland/patterns" "^0.0.2"
|
||||
|
||||
ton-crypto-primitives@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ton-crypto-primitives/-/ton-crypto-primitives-2.0.0.tgz#e85cd68c0d523f6bdf3f306201a76e51b7e9312e"
|
||||
integrity sha512-K+qKjpS0h9sPW6oExcpxnzuQ7nEgHEiDKwIqE/jWD25o8iFGe3FWj1gKxFNbKE9wwYKc5IV8FwrU+raF0KO5nQ==
|
||||
dependencies:
|
||||
jssha "3.2.0"
|
||||
|
||||
ton-crypto@2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/ton-crypto/-/ton-crypto-2.1.0.tgz#10b1de64a9cd92bde5a8a6678f952f49335c7e9a"
|
||||
integrity sha512-PZnmCOShfgq9tCRM8E7hG8nCkpkOyZvDLPXmZN92ZEBrfTT0NKKf0imndkxG5DkgWMjc6IKfgpnEaJDH9qN6ZQ==
|
||||
dependencies:
|
||||
jssha "3.2.0"
|
||||
ton-crypto-primitives "2.0.0"
|
||||
tweetnacl "1.0.3"
|
||||
|
||||
ton-crypto@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ton-crypto/-/ton-crypto-3.0.0.tgz#cc06c70c2c35918a84fc9770f24666e862a7df0f"
|
||||
integrity sha512-CMDXBhKvMBeIGp7I43E7d6vDSEwalObEAFzGjVZ9PFYEyxRXiqQIb0xht7GdLM5veirdbAVkVyVChZ/u1dy8ig==
|
||||
dependencies:
|
||||
jssha "3.2.0"
|
||||
ton-crypto-primitives "2.0.0"
|
||||
tweetnacl "1.0.3"
|
||||
|
||||
ton@9.8.0:
|
||||
version "9.8.0"
|
||||
resolved "https://registry.yarnpkg.com/ton/-/ton-9.8.0.tgz#28bc72276ebcacf9dff24f617505c55ccaa614ef"
|
||||
integrity sha512-aH/7ODiEulPRZracSfQ4KeaV6iDmzjn14+YMsZ9x0sNlzhtP4blMIFXPJFp4BOc4INb5vTrfA0u+JtOoXlBO0A==
|
||||
dependencies:
|
||||
axios "^0.25.0"
|
||||
bn.js "5.2.0"
|
||||
dataloader "^2.0.0"
|
||||
ethjs-unit "0.1.6"
|
||||
fp-ts "^2.11.1"
|
||||
io-ts "^2.2.16"
|
||||
io-ts-reporters "^2.0.0"
|
||||
symbol.inspect "1.0.1"
|
||||
teslabot "^1.3.0"
|
||||
ton-crypto "2.1.0"
|
||||
tweetnacl "1.0.3"
|
||||
|
||||
tweetnacl@1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596"
|
||||
integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==
|
||||
|
||||
typescript@^4.6.3:
|
||||
version "4.6.3"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.3.tgz#eefeafa6afdd31d725584c67a0eaba80f6fc6c6c"
|
||||
|
|
Loading…
Reference in New Issue