feat: Tonhub Connector

main
Steve Korshakov 2022-04-26 11:26:27 +04:00
parent 0e47abc7eb
commit 12d0837df9
6 changed files with 808 additions and 1 deletions

View File

@ -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"
}
}
}

View File

@ -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');
};
}

View File

@ -0,0 +1,15 @@
export {
TonhubCreatedSession,
TonhubSessionStateRevoked,
TonhubSessionStateExpired,
TonhubSessionStateReady,
TonhubSessionStateIniting,
TonhubSessionState,
TonhubSessionAwaited,
TonhubConnector,
TonhubTransactionRequest,
TonhubTransactionResponse,
TonhubSignRequest,
TonhubSignResponse,
TonhubWalletConfig
} from './connector/TonhubConnector';

6
src/utils/backoff.ts Normal file
View File

@ -0,0 +1,6 @@
import { createBackoff } from "teslabot";
export const backoff = createBackoff({
maxFailureCount: 5,
onError: (e, i) => i > 3 && console.warn(e)
});

12
src/utils/toURLsafe.ts Normal file
View File

@ -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
View File

@ -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"