@ton/ton 13.5.1 → 13.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/TonClient.d.ts +1 -19
- package/dist/client/TonClient.js +49 -24
- package/dist/client/TonClient4.d.ts +843 -0
- package/dist/client/TonClient4.js +138 -8
- package/dist/client/TonClient4.spec.js +7 -1
- package/dist/client/api/HttpApi.d.ts +17 -0
- package/dist/client/api/HttpApi.js +5 -2
- package/dist/config/ConfigParser.d.ts +622 -0
- package/dist/config/ConfigParser.js +711 -0
- package/dist/config/ConfigParser.spec.d.ts +8 -0
- package/dist/config/ConfigParser.spec.js +97 -0
- package/dist/elector/ElectorContract.d.ts +51 -0
- package/dist/elector/ElectorContract.js +192 -0
- package/dist/elector/ElectorContract.spec.d.ts +8 -0
- package/dist/elector/ElectorContract.spec.js +104 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +44 -1
- package/dist/multisig/MultisigWallet.d.ts +1 -0
- package/dist/multisig/MultisigWallet.js +14 -0
- package/dist/multisig/MultisigWallet.spec.js +18 -0
- package/dist/utils/fees.d.ts +25 -0
- package/dist/utils/fees.js +105 -0
- package/dist/utils/fees.spec.d.ts +1 -0
- package/dist/utils/fees.spec.js +83 -0
- package/dist/wallets/WalletContractV5.d.ts +111 -0
- package/dist/wallets/WalletContractV5.js +197 -0
- package/dist/wallets/WalletContractV5.spec.d.ts +8 -0
- package/dist/wallets/WalletContractV5.spec.js +151 -0
- package/dist/wallets/WalletV5Utils.d.ts +31 -0
- package/dist/wallets/WalletV5Utils.js +115 -0
- package/dist/wallets/WalletV5Utils.spec.d.ts +1 -0
- package/dist/wallets/WalletV5Utils.spec.js +192 -0
- package/dist/wallets/signing/createWalletTransfer.d.ts +7 -1
- package/dist/wallets/signing/createWalletTransfer.js +33 -1
- package/package.json +4 -4
- package/CHANGELOG.md +0 -133
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.computeMessageForwardFees = exports.computeExternalMessageFees = exports.computeGasPrices = exports.computeFwdFees = exports.computeStorageFees = void 0;
|
|
4
|
+
const core_1 = require("@ton/core");
|
|
5
|
+
//
|
|
6
|
+
// Source: https://github.com/ton-foundation/ton/blob/ae5c0720143e231c32c3d2034cfe4e533a16d969/crypto/block/transaction.cpp#L425
|
|
7
|
+
//
|
|
8
|
+
function computeStorageFees(data) {
|
|
9
|
+
const { lastPaid, now, storagePrices, storageStat, special, masterchain } = data;
|
|
10
|
+
if (now <= lastPaid || storagePrices.length === 0 || now < storagePrices[0].utime_since || special) {
|
|
11
|
+
return BigInt(0);
|
|
12
|
+
}
|
|
13
|
+
let upto = Math.max(lastPaid, storagePrices[0].utime_since);
|
|
14
|
+
let total = BigInt(0);
|
|
15
|
+
for (let i = 0; i < storagePrices.length && upto < now; i++) {
|
|
16
|
+
let valid_until = (i < storagePrices.length - 1 ? Math.min(now, storagePrices[i + 1].utime_since) : now);
|
|
17
|
+
let payment = BigInt(0);
|
|
18
|
+
if (upto < valid_until) {
|
|
19
|
+
let delta = valid_until - upto;
|
|
20
|
+
payment += (BigInt(storageStat.cells) * (masterchain ? storagePrices[i].mc_cell_price_ps : storagePrices[i].cell_price_ps));
|
|
21
|
+
payment += (BigInt(storageStat.bits) * (masterchain ? storagePrices[i].mc_bit_price_ps : storagePrices[i].bit_price_ps));
|
|
22
|
+
payment = payment * BigInt(delta);
|
|
23
|
+
}
|
|
24
|
+
upto = valid_until;
|
|
25
|
+
total += payment;
|
|
26
|
+
}
|
|
27
|
+
return shr16ceil(total);
|
|
28
|
+
}
|
|
29
|
+
exports.computeStorageFees = computeStorageFees;
|
|
30
|
+
//
|
|
31
|
+
// Source: https://github.com/ton-foundation/ton/blob/ae5c0720143e231c32c3d2034cfe4e533a16d969/crypto/block/transaction.cpp#L1218
|
|
32
|
+
//
|
|
33
|
+
function computeFwdFees(msgPrices, cells, bits) {
|
|
34
|
+
return msgPrices.lumpPrice + (shr16ceil(msgPrices.bitPrice * bits + (msgPrices.cellPrice * cells)));
|
|
35
|
+
}
|
|
36
|
+
exports.computeFwdFees = computeFwdFees;
|
|
37
|
+
//
|
|
38
|
+
// Source: https://github.com/ton-foundation/ton/blob/ae5c0720143e231c32c3d2034cfe4e533a16d969/crypto/block/transaction.cpp#L761
|
|
39
|
+
//
|
|
40
|
+
function computeGasPrices(gasUsed, prices) {
|
|
41
|
+
if (gasUsed <= prices.flatLimit) {
|
|
42
|
+
return prices.flatPrice;
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
// td::rshift(gas_price256 * (gas_used - cfg.flat_gas_limit), 16, 1) + cfg.flat_gas_price
|
|
46
|
+
return prices.flatPrice + ((prices.price * (gasUsed - prices.flatLimit)) >> 16n);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
exports.computeGasPrices = computeGasPrices;
|
|
50
|
+
//
|
|
51
|
+
// Source: https://github.com/ton-foundation/ton/blob/ae5c0720143e231c32c3d2034cfe4e533a16d969/crypto/block/transaction.cpp#L530
|
|
52
|
+
//
|
|
53
|
+
function computeExternalMessageFees(msgPrices, cell) {
|
|
54
|
+
// Collect stats
|
|
55
|
+
let storageStats = collectCellStats(cell);
|
|
56
|
+
storageStats.bits -= cell.bits.length;
|
|
57
|
+
storageStats.cells -= 1;
|
|
58
|
+
return computeFwdFees(msgPrices, BigInt(storageStats.cells), BigInt(storageStats.bits));
|
|
59
|
+
}
|
|
60
|
+
exports.computeExternalMessageFees = computeExternalMessageFees;
|
|
61
|
+
function computeMessageForwardFees(msgPrices, cell) {
|
|
62
|
+
let msg = (0, core_1.loadMessageRelaxed)(cell.beginParse());
|
|
63
|
+
let storageStats = { bits: 0, cells: 0 };
|
|
64
|
+
// Init
|
|
65
|
+
if (msg.init) {
|
|
66
|
+
const rawBuilder = new core_1.Cell().asBuilder();
|
|
67
|
+
(0, core_1.storeStateInit)(msg.init)(rawBuilder);
|
|
68
|
+
const raw = rawBuilder.endCell();
|
|
69
|
+
let c = collectCellStats(raw);
|
|
70
|
+
c.bits -= raw.bits.length;
|
|
71
|
+
c.cells -= 1;
|
|
72
|
+
storageStats.bits += c.bits;
|
|
73
|
+
storageStats.cells += c.cells;
|
|
74
|
+
}
|
|
75
|
+
// Body
|
|
76
|
+
let bc = collectCellStats(msg.body);
|
|
77
|
+
bc.bits -= msg.body.bits.length;
|
|
78
|
+
bc.cells -= 1;
|
|
79
|
+
storageStats.bits += bc.bits;
|
|
80
|
+
storageStats.cells += bc.cells;
|
|
81
|
+
// NOTE: Extra currencies are ignored for now
|
|
82
|
+
let fees = computeFwdFees(msgPrices, BigInt(storageStats.cells), BigInt(storageStats.bits));
|
|
83
|
+
let res = (fees * BigInt(msgPrices.firstFrac)) >> 16n;
|
|
84
|
+
let remaining = fees - res;
|
|
85
|
+
return { fees: res, remaining };
|
|
86
|
+
}
|
|
87
|
+
exports.computeMessageForwardFees = computeMessageForwardFees;
|
|
88
|
+
function collectCellStats(cell) {
|
|
89
|
+
let bits = cell.bits.length;
|
|
90
|
+
let cells = 1;
|
|
91
|
+
for (let ref of cell.refs) {
|
|
92
|
+
let r = collectCellStats(ref);
|
|
93
|
+
cells += r.cells;
|
|
94
|
+
bits += r.bits;
|
|
95
|
+
}
|
|
96
|
+
return { bits, cells };
|
|
97
|
+
}
|
|
98
|
+
function shr16ceil(src) {
|
|
99
|
+
let rem = src % 65536n;
|
|
100
|
+
let res = src >> 16n;
|
|
101
|
+
if (rem !== 0n) {
|
|
102
|
+
res += 1n;
|
|
103
|
+
}
|
|
104
|
+
return res;
|
|
105
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const fees_1 = require("./fees");
|
|
4
|
+
const core_1 = require("@ton/core");
|
|
5
|
+
const WalletContractV4_1 = require("../wallets/WalletContractV4");
|
|
6
|
+
describe('estimateFees', () => {
|
|
7
|
+
it('should estimate fees correctly', () => {
|
|
8
|
+
const config = {
|
|
9
|
+
storage: [{ utime_since: 0, bit_price_ps: BigInt(1), cell_price_ps: BigInt(500), mc_bit_price_ps: BigInt(1000), mc_cell_price_ps: BigInt(500000) }],
|
|
10
|
+
workchain: {
|
|
11
|
+
gas: { flatLimit: BigInt(100), flatGasPrice: BigInt(100000), price: BigInt(65536000) },
|
|
12
|
+
message: { lumpPrice: BigInt(1000000), bitPrice: BigInt(65536000), cellPrice: BigInt(6553600000), firstFrac: 21845 }
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
const storageStats = [{
|
|
16
|
+
lastPaid: 1696792239, duePayment: null,
|
|
17
|
+
used: { bits: 6888, cells: 14, publicCells: 0 }
|
|
18
|
+
}];
|
|
19
|
+
const gasUsageByOutMsgs = { 1: 3308, 2: 3950, 3: 4592, 4: 5234 };
|
|
20
|
+
const contract = WalletContractV4_1.WalletContractV4.create({ workchain: 0, publicKey: Buffer.from('MUP3GpbKCQu64L4PIU0QprZxmSUygHcaYKuo2tZYA1c=', 'base64') });
|
|
21
|
+
const body = (0, core_1.comment)('Test message fees estimation');
|
|
22
|
+
const testAddress = core_1.Address.parse('EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N');
|
|
23
|
+
// Create transfer
|
|
24
|
+
let intMessage = (0, core_1.internal)({
|
|
25
|
+
to: testAddress,
|
|
26
|
+
value: 1400000000n,
|
|
27
|
+
bounce: true,
|
|
28
|
+
body,
|
|
29
|
+
});
|
|
30
|
+
let transfer = contract.createTransfer({
|
|
31
|
+
seqno: 14,
|
|
32
|
+
secretKey: Buffer.alloc(64),
|
|
33
|
+
sendMode: core_1.SendMode.IGNORE_ERRORS | core_1.SendMode.PAY_GAS_SEPARATELY,
|
|
34
|
+
messages: [intMessage]
|
|
35
|
+
});
|
|
36
|
+
const externalMessage = (0, core_1.external)({
|
|
37
|
+
to: contract.address,
|
|
38
|
+
body: transfer,
|
|
39
|
+
init: null
|
|
40
|
+
});
|
|
41
|
+
let inMsg = new core_1.Cell().asBuilder();
|
|
42
|
+
(0, core_1.storeMessage)(externalMessage)(inMsg);
|
|
43
|
+
let outMsg = new core_1.Cell().asBuilder();
|
|
44
|
+
(0, core_1.storeMessageRelaxed)(intMessage)(outMsg);
|
|
45
|
+
// Storage fees
|
|
46
|
+
let storageFees = BigInt(0);
|
|
47
|
+
for (let storageStat of storageStats) {
|
|
48
|
+
if (storageStat) {
|
|
49
|
+
const computed = (0, fees_1.computeStorageFees)({
|
|
50
|
+
lastPaid: storageStat.lastPaid,
|
|
51
|
+
masterchain: false,
|
|
52
|
+
now: 1697445678,
|
|
53
|
+
special: false,
|
|
54
|
+
storagePrices: config.storage,
|
|
55
|
+
storageStat: {
|
|
56
|
+
bits: storageStat.used.bits,
|
|
57
|
+
cells: storageStat.used.cells,
|
|
58
|
+
publicCells: storageStat.used.publicCells
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
storageFees = storageFees + computed;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
expect((0, core_1.fromNano)(storageFees)).toBe('0.000138473');
|
|
65
|
+
// Calculate import fees
|
|
66
|
+
let importFees = (0, fees_1.computeExternalMessageFees)(config.workchain.message, inMsg.endCell());
|
|
67
|
+
expect((0, core_1.fromNano)(importFees)).toBe('0.001772');
|
|
68
|
+
// Any transaction use this amount of gas
|
|
69
|
+
const gasUsed = gasUsageByOutMsgs[1];
|
|
70
|
+
let gasFees = (0, fees_1.computeGasPrices)(BigInt(gasUsed), { flatLimit: config.workchain.gas.flatLimit, flatPrice: config.workchain.gas.flatGasPrice, price: config.workchain.gas.price });
|
|
71
|
+
expect((0, core_1.fromNano)(gasFees)).toBe('0.003308');
|
|
72
|
+
// Total
|
|
73
|
+
let total = BigInt(0);
|
|
74
|
+
total += storageFees;
|
|
75
|
+
total += importFees;
|
|
76
|
+
total += gasFees;
|
|
77
|
+
// Forward fees
|
|
78
|
+
let fwdFees = (0, fees_1.computeMessageForwardFees)(config.workchain.message, outMsg.endCell());
|
|
79
|
+
expect((0, core_1.fromNano)(fwdFees.fees)).toBe('0.000333328');
|
|
80
|
+
total += fwdFees.fees;
|
|
81
|
+
expect((0, core_1.fromNano)(total)).toBe('0.005551801');
|
|
82
|
+
});
|
|
83
|
+
});
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Whales Corp.
|
|
3
|
+
* All Rights Reserved.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
/// <reference types="node" />
|
|
9
|
+
import { Address, Cell, Contract, ContractProvider, MessageRelaxed, OutAction, Sender, SendMode } from "@ton/core";
|
|
10
|
+
import { Maybe } from "../utils/maybe";
|
|
11
|
+
import { OutActionExtended, WalletId } from "./WalletV5Utils";
|
|
12
|
+
export declare type Wallet5BasicSendArgs = {
|
|
13
|
+
seqno: number;
|
|
14
|
+
sendMode?: Maybe<SendMode>;
|
|
15
|
+
timeout?: Maybe<number>;
|
|
16
|
+
};
|
|
17
|
+
export declare type SingedAuthWallet5SendArgs = Wallet5BasicSendArgs & {
|
|
18
|
+
secretKey: Buffer;
|
|
19
|
+
};
|
|
20
|
+
export declare type ExtensionAuthWallet5SendArgs = Wallet5BasicSendArgs & {};
|
|
21
|
+
export declare type Wallet5SendArgs = SingedAuthWallet5SendArgs | ExtensionAuthWallet5SendArgs;
|
|
22
|
+
export declare class WalletContractV5 implements Contract {
|
|
23
|
+
readonly walletId: WalletId;
|
|
24
|
+
readonly publicKey: Buffer;
|
|
25
|
+
static opCodes: {
|
|
26
|
+
auth_extension: number;
|
|
27
|
+
auth_signed: number;
|
|
28
|
+
};
|
|
29
|
+
static create(args: {
|
|
30
|
+
walletId?: Partial<WalletId>;
|
|
31
|
+
publicKey: Buffer;
|
|
32
|
+
}): WalletContractV5;
|
|
33
|
+
readonly address: Address;
|
|
34
|
+
readonly init: {
|
|
35
|
+
data: Cell;
|
|
36
|
+
code: Cell;
|
|
37
|
+
};
|
|
38
|
+
private constructor();
|
|
39
|
+
/**
|
|
40
|
+
* Get Wallet Balance
|
|
41
|
+
*/
|
|
42
|
+
getBalance(provider: ContractProvider): Promise<bigint>;
|
|
43
|
+
/**
|
|
44
|
+
* Get Wallet Seqno
|
|
45
|
+
*/
|
|
46
|
+
getSeqno(provider: ContractProvider): Promise<number>;
|
|
47
|
+
/**
|
|
48
|
+
* Get Wallet Extensions
|
|
49
|
+
*/
|
|
50
|
+
getExtensions(provider: ContractProvider): Promise<Cell | null>;
|
|
51
|
+
/**
|
|
52
|
+
* Get Wallet Extensions
|
|
53
|
+
*/
|
|
54
|
+
getExtensionsArray(provider: ContractProvider): Promise<Address[]>;
|
|
55
|
+
/**
|
|
56
|
+
* Send signed transfer
|
|
57
|
+
*/
|
|
58
|
+
send(provider: ContractProvider, message: Cell): Promise<void>;
|
|
59
|
+
/**
|
|
60
|
+
* Sign and send transfer
|
|
61
|
+
*/
|
|
62
|
+
sendTransfer(provider: ContractProvider, args: Wallet5SendArgs & {
|
|
63
|
+
messages: MessageRelaxed[];
|
|
64
|
+
}): Promise<void>;
|
|
65
|
+
/**
|
|
66
|
+
* Sign and send add extension request
|
|
67
|
+
*/
|
|
68
|
+
sendAddExtension(provider: ContractProvider, args: Wallet5SendArgs & {
|
|
69
|
+
extensionAddress: Address;
|
|
70
|
+
}): Promise<void>;
|
|
71
|
+
/**
|
|
72
|
+
* Sign and send remove extension request
|
|
73
|
+
*/
|
|
74
|
+
sendRemoveExtension(provider: ContractProvider, args: Wallet5SendArgs & {
|
|
75
|
+
extensionAddress: Address;
|
|
76
|
+
}): Promise<void>;
|
|
77
|
+
/**
|
|
78
|
+
* Sign and send request
|
|
79
|
+
*/
|
|
80
|
+
sendRequest(provider: ContractProvider, args: Wallet5SendArgs & {
|
|
81
|
+
actions: (OutAction | OutActionExtended)[];
|
|
82
|
+
}): Promise<void>;
|
|
83
|
+
/**
|
|
84
|
+
* Create signed transfer
|
|
85
|
+
*/
|
|
86
|
+
createTransfer(args: Wallet5SendArgs & {
|
|
87
|
+
messages: MessageRelaxed[];
|
|
88
|
+
}): Cell;
|
|
89
|
+
/**
|
|
90
|
+
* Create signed add extension request
|
|
91
|
+
*/
|
|
92
|
+
createAddExtension(args: Wallet5SendArgs & {
|
|
93
|
+
extensionAddress: Address;
|
|
94
|
+
}): Cell;
|
|
95
|
+
/**
|
|
96
|
+
* Create signed remove extension request
|
|
97
|
+
*/
|
|
98
|
+
createRemoveExtension(args: Wallet5SendArgs & {
|
|
99
|
+
extensionAddress: Address;
|
|
100
|
+
}): Cell;
|
|
101
|
+
/**
|
|
102
|
+
* Create signed request
|
|
103
|
+
*/
|
|
104
|
+
createRequest(args: Wallet5SendArgs & {
|
|
105
|
+
actions: (OutAction | OutActionExtended)[];
|
|
106
|
+
}): Cell;
|
|
107
|
+
/**
|
|
108
|
+
* Create sender
|
|
109
|
+
*/
|
|
110
|
+
sender(provider: ContractProvider, secretKey: Buffer): Sender;
|
|
111
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) Whales Corp.
|
|
4
|
+
* All Rights Reserved.
|
|
5
|
+
*
|
|
6
|
+
* This source code is licensed under the MIT license found in the
|
|
7
|
+
* LICENSE file in the root directory of this source tree.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.WalletContractV5 = void 0;
|
|
11
|
+
const core_1 = require("@ton/core");
|
|
12
|
+
const createWalletTransfer_1 = require("./signing/createWalletTransfer");
|
|
13
|
+
const WalletV5Utils_1 = require("./WalletV5Utils");
|
|
14
|
+
class WalletContractV5 {
|
|
15
|
+
constructor(walletId, publicKey) {
|
|
16
|
+
this.walletId = walletId;
|
|
17
|
+
this.publicKey = publicKey;
|
|
18
|
+
this.walletId = walletId;
|
|
19
|
+
// Build initial code and data
|
|
20
|
+
let code = core_1.Cell.fromBoc(Buffer.from('te6cckEBAQEAIwAIQgLND3fEdsoVqej99mmdJbaOAOcmH9K3vkNG64R7FPAsl9kimVw=', 'base64'))[0];
|
|
21
|
+
let data = (0, core_1.beginCell)()
|
|
22
|
+
.storeUint(0, 32) // Seqno
|
|
23
|
+
.store((0, WalletV5Utils_1.storeWalletId)(this.walletId))
|
|
24
|
+
.storeBuffer(this.publicKey)
|
|
25
|
+
.storeBit(0) // Empty plugins dict
|
|
26
|
+
.endCell();
|
|
27
|
+
this.init = { code, data };
|
|
28
|
+
this.address = (0, core_1.contractAddress)(this.walletId.workChain, { code, data });
|
|
29
|
+
}
|
|
30
|
+
static create(args) {
|
|
31
|
+
const walletId = {
|
|
32
|
+
networkGlobalId: args.walletId?.networkGlobalId ?? -239,
|
|
33
|
+
workChain: args?.walletId?.workChain ?? 0,
|
|
34
|
+
subwalletNumber: args?.walletId?.subwalletNumber ?? 0,
|
|
35
|
+
walletVersion: args?.walletId?.walletVersion ?? 'v5'
|
|
36
|
+
};
|
|
37
|
+
return new WalletContractV5(walletId, args.publicKey);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Get Wallet Balance
|
|
41
|
+
*/
|
|
42
|
+
async getBalance(provider) {
|
|
43
|
+
let state = await provider.getState();
|
|
44
|
+
return state.balance;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Get Wallet Seqno
|
|
48
|
+
*/
|
|
49
|
+
async getSeqno(provider) {
|
|
50
|
+
let state = await provider.getState();
|
|
51
|
+
if (state.state.type === 'active') {
|
|
52
|
+
let res = await provider.get('seqno', []);
|
|
53
|
+
return res.stack.readNumber();
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
return 0;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Get Wallet Extensions
|
|
61
|
+
*/
|
|
62
|
+
async getExtensions(provider) {
|
|
63
|
+
let state = await provider.getState();
|
|
64
|
+
if (state.state.type === 'active') {
|
|
65
|
+
const result = await provider.get('get_extensions', []);
|
|
66
|
+
return result.stack.readCellOpt();
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Get Wallet Extensions
|
|
74
|
+
*/
|
|
75
|
+
async getExtensionsArray(provider) {
|
|
76
|
+
const extensions = await this.getExtensions(provider);
|
|
77
|
+
if (!extensions) {
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
80
|
+
const dict = core_1.Dictionary.loadDirect(core_1.Dictionary.Keys.BigUint(256), core_1.Dictionary.Values.BigInt(8), extensions);
|
|
81
|
+
return dict.keys().map(key => {
|
|
82
|
+
const wc = dict.get(key);
|
|
83
|
+
const addressHex = key ^ (wc + 1n);
|
|
84
|
+
return core_1.Address.parseRaw(`${wc}:${addressHex.toString(16)}`);
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Send signed transfer
|
|
89
|
+
*/
|
|
90
|
+
async send(provider, message) {
|
|
91
|
+
await provider.external(message);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Sign and send transfer
|
|
95
|
+
*/
|
|
96
|
+
async sendTransfer(provider, args) {
|
|
97
|
+
const transfer = this.createTransfer(args);
|
|
98
|
+
await this.send(provider, transfer);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Sign and send add extension request
|
|
102
|
+
*/
|
|
103
|
+
async sendAddExtension(provider, args) {
|
|
104
|
+
const request = this.createAddExtension(args);
|
|
105
|
+
await this.send(provider, request);
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Sign and send remove extension request
|
|
109
|
+
*/
|
|
110
|
+
async sendRemoveExtension(provider, args) {
|
|
111
|
+
const request = this.createRemoveExtension(args);
|
|
112
|
+
await this.send(provider, request);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Sign and send request
|
|
116
|
+
*/
|
|
117
|
+
async sendRequest(provider, args) {
|
|
118
|
+
const request = this.createRequest(args);
|
|
119
|
+
await this.send(provider, request);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Create signed transfer
|
|
123
|
+
*/
|
|
124
|
+
createTransfer(args) {
|
|
125
|
+
const { messages, ...rest } = args;
|
|
126
|
+
const sendMode = args.sendMode ?? core_1.SendMode.PAY_GAS_SEPARATELY;
|
|
127
|
+
const actions = messages.map(message => ({ type: 'sendMsg', mode: sendMode, outMsg: message }));
|
|
128
|
+
return this.createRequest({
|
|
129
|
+
...rest,
|
|
130
|
+
actions
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Create signed add extension request
|
|
135
|
+
*/
|
|
136
|
+
createAddExtension(args) {
|
|
137
|
+
const { extensionAddress, ...rest } = args;
|
|
138
|
+
return this.createRequest({
|
|
139
|
+
actions: [{
|
|
140
|
+
type: 'addExtension',
|
|
141
|
+
address: extensionAddress
|
|
142
|
+
}],
|
|
143
|
+
...rest
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Create signed remove extension request
|
|
148
|
+
*/
|
|
149
|
+
createRemoveExtension(args) {
|
|
150
|
+
const { extensionAddress, ...rest } = args;
|
|
151
|
+
return this.createRequest({
|
|
152
|
+
actions: [{
|
|
153
|
+
type: 'removeExtension',
|
|
154
|
+
address: extensionAddress
|
|
155
|
+
}],
|
|
156
|
+
...rest
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Create signed request
|
|
161
|
+
*/
|
|
162
|
+
createRequest(args) {
|
|
163
|
+
return (0, createWalletTransfer_1.createWalletTransferV5)({
|
|
164
|
+
...args,
|
|
165
|
+
sendMode: args.sendMode ?? core_1.SendMode.PAY_GAS_SEPARATELY,
|
|
166
|
+
walletId: (0, WalletV5Utils_1.storeWalletId)(this.walletId)
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Create sender
|
|
171
|
+
*/
|
|
172
|
+
sender(provider, secretKey) {
|
|
173
|
+
return {
|
|
174
|
+
send: async (args) => {
|
|
175
|
+
let seqno = await this.getSeqno(provider);
|
|
176
|
+
let transfer = this.createTransfer({
|
|
177
|
+
seqno,
|
|
178
|
+
secretKey,
|
|
179
|
+
sendMode: args.sendMode,
|
|
180
|
+
messages: [(0, core_1.internal)({
|
|
181
|
+
to: args.to,
|
|
182
|
+
value: args.value,
|
|
183
|
+
init: args.init,
|
|
184
|
+
body: args.body,
|
|
185
|
+
bounce: args.bounce
|
|
186
|
+
})]
|
|
187
|
+
});
|
|
188
|
+
await this.send(provider, transfer);
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
exports.WalletContractV5 = WalletContractV5;
|
|
194
|
+
WalletContractV5.opCodes = {
|
|
195
|
+
auth_extension: 0x6578746e,
|
|
196
|
+
auth_signed: 0x7369676e
|
|
197
|
+
};
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) Whales Corp.
|
|
4
|
+
* All Rights Reserved.
|
|
5
|
+
*
|
|
6
|
+
* This source code is licensed under the MIT license found in the
|
|
7
|
+
* LICENSE file in the root directory of this source tree.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
const randomTestKey_1 = require("../utils/randomTestKey");
|
|
11
|
+
const core_1 = require("@ton/core");
|
|
12
|
+
const WalletContractV5_1 = require("./WalletContractV5");
|
|
13
|
+
const createTestClient_1 = require("../utils/createTestClient");
|
|
14
|
+
const getExtensionsArray = async (wallet) => {
|
|
15
|
+
try {
|
|
16
|
+
return await wallet.getExtensionsArray();
|
|
17
|
+
}
|
|
18
|
+
catch (e) {
|
|
19
|
+
// Handle toncenter bug. Toncenter incorrectly returns 'list' in the stack in case of empty extensions dict
|
|
20
|
+
if (e instanceof Error && e.message === 'Unsupported stack item type: list') {
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
throw e;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
describe('WalletContractV5', () => {
|
|
27
|
+
let client;
|
|
28
|
+
let walletKey;
|
|
29
|
+
let wallet;
|
|
30
|
+
beforeEach(() => {
|
|
31
|
+
client = (0, createTestClient_1.createTestClient)();
|
|
32
|
+
walletKey = (0, randomTestKey_1.randomTestKey)('v5-treasure');
|
|
33
|
+
wallet = client.open(WalletContractV5_1.WalletContractV5.create({ walletId: { networkGlobalId: -3 }, publicKey: walletKey.publicKey }));
|
|
34
|
+
});
|
|
35
|
+
it('should has balance and correct address', async () => {
|
|
36
|
+
const balance = await wallet.getBalance();
|
|
37
|
+
expect(wallet.address.equals(core_1.Address.parse('EQDv2B0jPmJZ1j-ne3Ko64eGqfYZRHGQbfSE5pUWVvUdQmDH'))).toBeTruthy();
|
|
38
|
+
expect(balance > 0n).toBe(true);
|
|
39
|
+
});
|
|
40
|
+
it('should perform single transfer', async () => {
|
|
41
|
+
const seqno = await wallet.getSeqno();
|
|
42
|
+
const transfer = wallet.createTransfer({
|
|
43
|
+
seqno,
|
|
44
|
+
secretKey: walletKey.secretKey,
|
|
45
|
+
messages: [(0, core_1.internal)({
|
|
46
|
+
to: 'EQDQ0PRYSWmW-v6LVHNYq5Uelpr5f7Ct7awG7Lao2HImrCzn',
|
|
47
|
+
value: '0.01',
|
|
48
|
+
body: 'Hello world single transfer!'
|
|
49
|
+
})]
|
|
50
|
+
});
|
|
51
|
+
await wallet.send(transfer);
|
|
52
|
+
});
|
|
53
|
+
it('should perform double transfer', async () => {
|
|
54
|
+
const seqno = await wallet.getSeqno();
|
|
55
|
+
const transfer = wallet.createTransfer({
|
|
56
|
+
seqno,
|
|
57
|
+
secretKey: walletKey.secretKey,
|
|
58
|
+
messages: [(0, core_1.internal)({
|
|
59
|
+
to: 'EQDQ0PRYSWmW-v6LVHNYq5Uelpr5f7Ct7awG7Lao2HImrCzn',
|
|
60
|
+
value: '0.01',
|
|
61
|
+
body: 'Hello world to extension'
|
|
62
|
+
}), (0, core_1.internal)({
|
|
63
|
+
to: 'EQAtHiE_vEyAogU1rHcz3uzp64h-yqeFJ2S2ChkKNwygLMk3',
|
|
64
|
+
value: '0.02',
|
|
65
|
+
body: 'Hello world to relayer'
|
|
66
|
+
})]
|
|
67
|
+
});
|
|
68
|
+
await wallet.send(transfer);
|
|
69
|
+
});
|
|
70
|
+
it('should add extension', async () => {
|
|
71
|
+
const extensionKey = (0, randomTestKey_1.randomTestKey)('v5-treasure-extension');
|
|
72
|
+
const extensionContract = client.open(WalletContractV5_1.WalletContractV5.create({ walletId: { workChain: 0, networkGlobalId: -3 }, publicKey: extensionKey.publicKey }));
|
|
73
|
+
const seqno = await wallet.getSeqno();
|
|
74
|
+
const extensions = await getExtensionsArray(wallet);
|
|
75
|
+
const extensionAlreadyAdded = extensions.some(address => address.equals(extensionContract.address));
|
|
76
|
+
if (!extensionAlreadyAdded) {
|
|
77
|
+
await wallet.sendAddExtension({
|
|
78
|
+
seqno,
|
|
79
|
+
secretKey: walletKey.secretKey,
|
|
80
|
+
extensionAddress: extensionContract.address
|
|
81
|
+
});
|
|
82
|
+
const waitUntilExtensionAdded = async (attempt = 0) => {
|
|
83
|
+
if (attempt >= 10) {
|
|
84
|
+
throw new Error('Extension was not added in 10 blocks');
|
|
85
|
+
}
|
|
86
|
+
const extensions = await getExtensionsArray(wallet);
|
|
87
|
+
const extensionAdded = extensions.some(address => address.equals(extensionContract.address));
|
|
88
|
+
if (extensionAdded) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
await new Promise(r => setTimeout(r, 1500));
|
|
92
|
+
return waitUntilExtensionAdded(attempt + 1);
|
|
93
|
+
};
|
|
94
|
+
await waitUntilExtensionAdded();
|
|
95
|
+
}
|
|
96
|
+
const extensionsSeqno = await extensionContract.getSeqno();
|
|
97
|
+
await extensionContract.sendTransfer({
|
|
98
|
+
seqno: extensionsSeqno,
|
|
99
|
+
secretKey: extensionKey.secretKey,
|
|
100
|
+
messages: [(0, core_1.internal)({
|
|
101
|
+
to: wallet.address,
|
|
102
|
+
value: '0.1',
|
|
103
|
+
body: wallet.createTransfer({
|
|
104
|
+
seqno: seqno + 1,
|
|
105
|
+
messages: [(0, core_1.internal)({
|
|
106
|
+
to: 'kQD6oPnzaaAMRW24R8F0_nlSsJQni0cGHntR027eT9_sgtwt',
|
|
107
|
+
value: '0.03',
|
|
108
|
+
body: 'Hello world from plugin'
|
|
109
|
+
})]
|
|
110
|
+
})
|
|
111
|
+
})]
|
|
112
|
+
});
|
|
113
|
+
}, 60000);
|
|
114
|
+
it('should remove extension', async () => {
|
|
115
|
+
const extensionKey = (0, randomTestKey_1.randomTestKey)('v5-treasure-extension');
|
|
116
|
+
const extensionContract = client.open(WalletContractV5_1.WalletContractV5.create({ walletId: { workChain: 0, networkGlobalId: -3 }, publicKey: extensionKey.publicKey }));
|
|
117
|
+
const seqno = await wallet.getSeqno();
|
|
118
|
+
const extensions = await getExtensionsArray(wallet);
|
|
119
|
+
const extensionAlreadyAdded = extensions.some(address => address.equals(extensionContract.address));
|
|
120
|
+
if (extensionAlreadyAdded) {
|
|
121
|
+
await wallet.sendRemoveExtension({
|
|
122
|
+
seqno,
|
|
123
|
+
secretKey: walletKey.secretKey,
|
|
124
|
+
extensionAddress: extensionContract.address
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
it('should send internal transfer via relayer', async () => {
|
|
129
|
+
const relaerKey = (0, randomTestKey_1.randomTestKey)('v5-treasure-relayer');
|
|
130
|
+
const relayerContract = client.open(WalletContractV5_1.WalletContractV5.create({ walletId: { workChain: 0, networkGlobalId: -3 }, publicKey: relaerKey.publicKey }));
|
|
131
|
+
const seqno = await wallet.getSeqno();
|
|
132
|
+
const relayerSeqno = await relayerContract.getSeqno();
|
|
133
|
+
await relayerContract.sendTransfer({
|
|
134
|
+
seqno: relayerSeqno,
|
|
135
|
+
secretKey: relaerKey.secretKey,
|
|
136
|
+
messages: [(0, core_1.internal)({
|
|
137
|
+
to: wallet.address,
|
|
138
|
+
value: '0.1',
|
|
139
|
+
body: wallet.createTransfer({
|
|
140
|
+
seqno: seqno,
|
|
141
|
+
secretKey: walletKey.secretKey,
|
|
142
|
+
messages: [(0, core_1.internal)({
|
|
143
|
+
to: 'kQD6oPnzaaAMRW24R8F0_nlSsJQni0cGHntR027eT9_sgtwt',
|
|
144
|
+
value: '0.04',
|
|
145
|
+
body: 'Hello world from relayer'
|
|
146
|
+
})]
|
|
147
|
+
})
|
|
148
|
+
})]
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { Address, Builder, Cell, OutAction, Slice } from '@ton/core';
|
|
3
|
+
export interface OutActionSetData {
|
|
4
|
+
type: 'setData';
|
|
5
|
+
newData: Cell;
|
|
6
|
+
}
|
|
7
|
+
export interface OutActionAddExtension {
|
|
8
|
+
type: 'addExtension';
|
|
9
|
+
address: Address;
|
|
10
|
+
}
|
|
11
|
+
export interface OutActionRemoveExtension {
|
|
12
|
+
type: 'removeExtension';
|
|
13
|
+
address: Address;
|
|
14
|
+
}
|
|
15
|
+
export declare type OutActionExtended = OutActionSetData | OutActionAddExtension | OutActionRemoveExtension;
|
|
16
|
+
export declare function storeOutActionExtended(action: OutActionExtended): (builder: Builder) => void;
|
|
17
|
+
export declare function loadOutActionExtended(slice: Slice): OutActionExtended;
|
|
18
|
+
export declare function isOutActionExtended(action: OutAction | OutActionExtended): action is OutActionExtended;
|
|
19
|
+
export declare function storeOutListExtended(actions: (OutActionExtended | OutAction)[]): (builder: Builder) => void;
|
|
20
|
+
export declare function loadOutListExtended(slice: Slice): (OutActionExtended | OutAction)[];
|
|
21
|
+
export interface WalletId {
|
|
22
|
+
readonly walletVersion: 'v5';
|
|
23
|
+
/**
|
|
24
|
+
* -239 is mainnet, -3 is testnet
|
|
25
|
+
*/
|
|
26
|
+
readonly networkGlobalId: number;
|
|
27
|
+
readonly workChain: number;
|
|
28
|
+
readonly subwalletNumber: number;
|
|
29
|
+
}
|
|
30
|
+
export declare function loadWalletId(value: bigint | Buffer | Slice): WalletId;
|
|
31
|
+
export declare function storeWalletId(walletId: WalletId): (builder: Builder) => void;
|