smc upload

This commit is contained in:
gillgate 2024-09-10 11:46:05 +03:00
parent 3bb5c30b2b
commit e3acca0523
20 changed files with 6548 additions and 127 deletions

140
.gitignore vendored
View File

@ -1,132 +1,18 @@
# ---> Node
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
node_modules
temp
dist
.DS_Store
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# VS Code
.vscode/*
.history/
*.vsix
# vuepress build output
.vuepress/dist
# IDEA files
.idea
# vuepress v2.x temp and cache directory
.temp
.cache
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
# VIM
Session.vim
contracts
*.txt

1
.prettierignore Normal file
View File

@ -0,0 +1 @@
build

7
.prettierrc Normal file
View File

@ -0,0 +1,7 @@
{
"printWidth": 120,
"tabWidth": 4,
"singleQuote": true,
"bracketSpacing": true,
"semi": true
}

View File

@ -1,2 +1,18 @@
# rps-project
## Project structure
- `config` - config file with all constants and state init for working with contract
- `wrappers` - wrapper classes (implementing `Contract` from ton-core) for the contracts, including any [de]serialization primitives and compilation functions.
- `tests` - tests for the contracts.
- `scripts` - scripts used by the project, mainly the deployment scripts.
## How to use
### Test
`npx blueprint test`
### Deploy or run selected script
`npx blueprint run`

View File

@ -0,0 +1 @@
{"hash":"30afc256a9ab06895264b563ce9c62abad61405d10ac88154956869e98c90fc5","hashBase64":"MK/CVqmrBolSZLVjzpxiq61hQF0QrIgVSVaGnpjJD8U=","hex":"b5ee9c7241021201000210000114ff00f4a413f4bcf2c80b0102016202090202ce030803f743322c700925f03e0d0d3030171b0925f03e0fa403002d31fed44d0fa40fa00fa40fa403025c0018e1c343435037aa90414a04013c85004cf1658fa0201cf1601cf16c9ed54e03624c0028e21345055c705f2e193fa403020f0024313c85004cf1658fa0201cf1601cf16c9ed54e024c003e30224c004e3025b33018040507005230335144c705f2e193fa403020d749c202f2e19420f00258c85004cf1658fa0201cf1601cf16c9ed5401fa345151c705f2e19324c000f2d19101fa00fa00305ca05006bcf2d19252208bb53657276696365206665658708018c8cb055004cf1658fa027001cb6a12cb1f01cf16c971fb0052248d05541c9bdd9a5919481b1a5c5d5a591a5d1e4819995960708018c8cb055004cf1658fa027001cb6a12cb1f01cf16c971fb007002060022c85004cf1658fa0201cf1601cf16c9ed540086c0058e3859c705f2e193fa40fa003082080f424070fb028b857697468647261778708018c8cb055004cf1658fa027001cb6a12cb1f01cf16c971fb00e05f038417f2f000114fa4430c000f2e14d80201200a0f0201480b0c0021b67a1da89a1f481f401f481f48060d84500201200d0e0025b2017b51343e903e803e903e900c0408d7c0e0000fb0cbfe09dbc88c200202731011001dac40f6a2687d207d007d207d2018400021afddf6a2687d207d007d207d20182f81c010696ed8"}

37
config/contract.config.ts Normal file
View File

@ -0,0 +1,37 @@
import { MainBalanceConfig } from "../wrappers/MainBalance";
export const servicePercent = 5; // %
export const tokenPercent = 5; // %
export default {
op: {
DEPOSIT: 1,
CHANGE_OWNER_ADDRESS: 2,
CHANGE_SERVICE_ADDRESS: 3,
TRANSFER_SERVICE_FEE: 4,
WITHDRAW: 5
},
error: {
WRONG_WORKCHAIN: 333,
FUNDS_EMPTY: 401,
FUNDS_EXCEED: 402,
INVALID_OWNER: 403,
EMPTY_ADDRESS: 404,
UNKNOWN_OP: 0xffffff
},
totalFee: servicePercent + tokenPercent, // %
init: {
ownerAddress: "0QBX_oJ8xet3pvRJ6JK1GHq9oUiQjct0ohQlgYX1OGopNcOP",
depositFunds: 0,
serviceAddress: "0QBX_oJ8xet3pvRJ6JK1GHq9oUiQjct0ohQlgYX1OGopNcOP",
tokenAddress: "0QC-0_RK1mBAc-DRAn9LHsLNt2NcLB_eNIJ1UicLs63C0ri4"
} as MainBalanceConfig,
script: {
depositAddress: "0QC-0_RK1mBAc-DRAn9LHsLNt2NcLB_eNIJ1UicLs63C0ri4",
depositAmount: 1, // TON
newOwnerAddress: "0QC-0_RK1mBAc-DRAn9LHsLNt2NcLB_eNIJ1UicLs63C0ri4",
newServiceAddress: "0QC-0_RK1mBAc-DRAn9LHsLNt2NcLB_eNIJ1UicLs63C0ri4"
}
}

9
jest.config.ts Normal file
View File

@ -0,0 +1,9 @@
import type { Config } from 'jest';
const config: Config = {
preset: 'ts-jest',
testEnvironment: 'node',
testPathIgnorePatterns: ['/node_modules/', '/dist/'],
};
export default config;

5984
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

24
package.json Normal file
View File

@ -0,0 +1,24 @@
{
"name": "rps-project",
"version": "0.0.1",
"scripts": {
"start": "blueprint run",
"build": "blueprint build",
"test": "jest --verbose"
},
"devDependencies": {
"@ton/blueprint": "^0.22.0",
"@ton/sandbox": "^0.20.0",
"@ton/test-utils": "^0.4.2",
"@types/jest": "^29.5.12",
"@types/node": "^20.14.10",
"jest": "^29.7.0",
"prettier": "^3.3.2",
"@ton/ton": "^13.11.2",
"@ton/core": "~0.57.0",
"@ton/crypto": "^3.2.0",
"ts-jest": "^29.2.0",
"ts-node": "^10.9.2",
"typescript": "^5.5.3"
}
}

View File

@ -0,0 +1,13 @@
import { toNano } from '@ton/core';
import { MainBalance } from '../wrappers/MainBalance';
import { compile, NetworkProvider } from '@ton/blueprint';
import contractConfig from '../config/contract.config';
import { getMainBalanceContract } from './utils';
export async function run(provider: NetworkProvider) {
const mainBalance = await getMainBalanceContract(provider);
await mainBalance.sendDeploy(provider.sender(), toNano('0.05'));
await provider.waitForDeploy(mainBalance.address);
}

View File

@ -0,0 +1,14 @@
import { toNano } from '@ton/core';
import { MainBalance } from '../wrappers/MainBalance';
import { compile, NetworkProvider } from '@ton/blueprint';
import contractConfig from '../config/contract.config';
import { getMainBalanceContract } from './utils';
export async function run(provider: NetworkProvider) {
const ui = provider.ui();
const mainBalance = await getMainBalanceContract(provider);
ui.write(`Current contract balance: ${await mainBalance.getBalance()}`);
await mainBalance.sendDeploy(provider.sender(), toNano(contractConfig.script.depositAmount));
ui.write(`${contractConfig.script.depositAmount} TON has been sent to smc address: ${mainBalance.address}`);
}

View File

@ -0,0 +1,15 @@
import { Address, toNano } from '@ton/core';
import { MainBalance } from '../wrappers/MainBalance';
import { compile, NetworkProvider } from '@ton/blueprint';
import contractConfig from '../config/contract.config';
import { getMainBalanceContract } from './utils';
export async function run(provider: NetworkProvider) {
const ui = provider.ui();
const mainBalance = await getMainBalanceContract(provider);
ui.write(`prev owner address: ${await mainBalance.getOwnerAddress()}`);
await mainBalance.sendChangeOwnerAddress(provider.sender(), Address.parse(contractConfig.script.newOwnerAddress));
ui.write(`current owner address: ${contractConfig.script.newOwnerAddress}`);
}

View File

@ -0,0 +1,20 @@
import { Address, toNano } from '@ton/core';
import { MainBalance } from '../wrappers/MainBalance';
import { compile, NetworkProvider } from '@ton/blueprint';
import contractConfig from '../config/contract.config';
import { getMainBalanceContract } from './utils';
export async function run(provider: NetworkProvider) {
const ui = provider.ui();
const mainBalance = await getMainBalanceContract(provider);
ui.write(`prev service address: ${await mainBalance.getServiceAddress()}`);
await mainBalance.sendChangeServiceAddress(
provider.sender(),
Address.parse(contractConfig.script.newServiceAddress),
Address.parse(contractConfig.init.tokenAddress)
);
ui.write(`current service address: ${contractConfig.script.newServiceAddress}`);
}

View File

@ -0,0 +1,22 @@
import { toNano } from '@ton/core';
import { MainBalance } from '../wrappers/MainBalance';
import { compile, NetworkProvider } from '@ton/blueprint';
import contractConfig from '../config/contract.config';
import { calcPercent, getMainBalanceContract } from './utils';
export async function run(provider: NetworkProvider) {
const ui = provider.ui();
const mainBalance = await getMainBalanceContract(provider);
const currentDepositFunds = await mainBalance.getDepositFunds();
const { serviceAddr, tokenAddr } = await mainBalance.getServiceAddress();
ui.write(`Current contract balance: ${await mainBalance.getBalance()}`);
ui.write(`Current deposit funds: ${currentDepositFunds}`);
// сумма этих двух не должна превышать deposit funds иначе контракт выдаст ошибку
await mainBalance.sendTransferServiceFee(provider.sender(), toNano(currentDepositFunds / 2), toNano(currentDepositFunds / 2));
ui.write(`50% Deposit funds was withdrawn to service address: ${serviceAddr}`);
ui.write(`50% Deposit funds was withdrawn to token address: ${tokenAddr}`);
}

View File

@ -0,0 +1,18 @@
import { Address, toNano } from '@ton/core';
import { MainBalance } from '../wrappers/MainBalance';
import { compile, NetworkProvider } from '@ton/blueprint';
import contractConfig from '../config/contract.config';
import { calcPercent, getMainBalanceContract } from './utils';
export async function run(provider: NetworkProvider) {
const ui = provider.ui();
const mainBalance = await getMainBalanceContract(provider);
const depositAddress = contractConfig.script.depositAddress;
const depositAmount = contractConfig.script.depositAmount;
const withdrawAmount = depositAmount - calcPercent(depositAmount, contractConfig.totalFee);
ui.write(`Current contract balance: ${await mainBalance.getBalance()}`);
await mainBalance.sendWithdraw(provider.sender(), Address.parse(depositAddress), toNano(withdrawAmount))
ui.write(`${depositAmount} - ${contractConfig.totalFee}% = ${withdrawAmount} TON was withdrawn to user address: ${depositAddress}`);
}

15
scripts/utils.ts Normal file
View File

@ -0,0 +1,15 @@
import { Address, Cell, toNano } from "@ton/core";
import { MainBalance } from "../wrappers/MainBalance";
import { hex } from "../build/MainBalance.compiled.json";
import { NetworkProvider } from "@ton/blueprint";
import contractConfig from "../config/contract.config";
export async function getMainBalanceContract(provider:NetworkProvider) {
const codeCell = Cell.fromBoc(Buffer.from(hex, "hex"))[0];
return provider.open(MainBalance.createFromConfig(contractConfig.init, codeCell));
}
export function calcPercent(amount:number, percent:number = 1) {
return (amount / 100) * percent;
}

190
tests/MainBalance.spec.ts Normal file
View File

@ -0,0 +1,190 @@
import { Blockchain, SandboxContract, TreasuryContract } from '@ton/sandbox';
import { Cell, toNano } from '@ton/core';
import { MainBalance } from '../wrappers/MainBalance';
import '@ton/test-utils';
import { compile } from '@ton/blueprint';
import { randomAddress } from '@ton/test-utils';
import { fromNano } from 'ton';
import { hex } from "../build/MainBalance.compiled.json";
import contractConfig, { servicePercent, tokenPercent } from '../config/contract.config';
import { calcPercent } from '../scripts/utils';
describe('MainBalance', () => {
let code: Cell;
beforeAll(async () => {
// code = await compile('MainBalance');
code = Cell.fromBoc(Buffer.from(hex, "hex"))[0];
});
let blockchain: Blockchain;
let owner: SandboxContract<TreasuryContract>;
let newOwner: SandboxContract<TreasuryContract>;
let user: SandboxContract<TreasuryContract>;
let anotherUser: SandboxContract<TreasuryContract>;
let mainBalance: SandboxContract<MainBalance>;
let serviceAddress: SandboxContract<TreasuryContract>;
let tokenAddress: SandboxContract<TreasuryContract>;
beforeEach(async () => {
blockchain = await Blockchain.create();
owner = await blockchain.treasury('owner');
newOwner = await blockchain.treasury('newOwner');
user = await blockchain.treasury('user');
anotherUser = await blockchain.treasury('anotherUser');
serviceAddress = await blockchain.treasury('serviceAddress');
tokenAddress = await blockchain.treasury('tokenAddress');
mainBalance = blockchain.openContract(MainBalance.createFromConfig({
ownerAddress: owner.address.toString(),
depositFunds: 0,
serviceAddress: serviceAddress.address.toString(),
tokenAddress: tokenAddress.address.toString()
}, code));
const deployResult = await mainBalance.sendDeploy(owner.getSender(), toNano(0.05));
expect(deployResult.transactions).toHaveTransaction({
from: owner.address,
to: mainBalance.address,
deploy: true,
success: true,
});
});
it('should deploy', async () => {
// the check is done inside beforeEach
// blockchain and mainBalance are ready to use
});
it("should deposit funds", async () => {
const depositResult = await mainBalance.sendDeposit(user.getSender(), toNano('10'));
expect(depositResult.transactions).toHaveTransaction({
from: user.address,
to: mainBalance.address,
success: true,
});
expect(+fromNano(await mainBalance.getBalance())).toBeCloseTo(10, 0.05);
expect(+fromNano(await mainBalance.getDepositFunds())).toBeCloseTo(calcPercent(10, contractConfig.totalFee), 0.05);
});
it("should change owner address", async () => {
await mainBalance.sendChangeOwnerAddress(owner.getSender(), newOwner.address);
expect((await mainBalance.getOwnerAddress()).toString()).toBe(newOwner.address.toString());
});
it("shouldn't change owner address", async () => {
await mainBalance.sendChangeOwnerAddress(user.getSender(), newOwner.address);
expect((await mainBalance.getOwnerAddress()).toString()).not.toBe(newOwner.address.toString());
});
it("should change only service address", async () => {
const changeResult = await mainBalance.sendChangeServiceAddress(owner.getSender(), newOwner.address, tokenAddress.address);
const { serviceAddr } = await mainBalance.getServiceAddress();
expect(changeResult.transactions).toHaveTransaction({
from: owner.address,
to: mainBalance.address,
success: true,
});
expect(serviceAddr.toString()).toBe(newOwner.address.toString());
});
it("should transfer service fee", async () => {
await mainBalance.sendDeposit(user.getSender(), toNano(10));
await mainBalance.sendDeposit(user.getSender(), toNano(20));
await mainBalance.sendDeposit(anotherUser.getSender(), toNano(50));
let totalBalance = 10 + 20 + 50; // 80
const serviceFee = calcPercent(totalBalance, servicePercent);
const tokenFee = calcPercent(totalBalance, tokenPercent);
const tranferResult = await mainBalance.sendTransferServiceFee(owner.getSender(), toNano(serviceFee), toNano(tokenFee));
expect(tranferResult.transactions).toHaveTransaction({
from: owner.address,
to: mainBalance.address,
success: true,
});
expect(+fromNano(await mainBalance.getDepositFunds())).toBe(0);
expect(tranferResult.transactions).toHaveTransaction({
from: mainBalance.address,
to: serviceAddress.address,
success: true,
});
expect(tranferResult.transactions).toHaveTransaction({
from: mainBalance.address,
to: tokenAddress.address,
success: true,
});
});
it("shouldn't transfer service fee when funds exceeded", async () => {
await mainBalance.sendDeposit(user.getSender(), toNano(10));
await mainBalance.sendDeposit(user.getSender(), toNano(20));
await mainBalance.sendDeposit(anotherUser.getSender(), toNano(50));
let totalBalance = 10 + 20 + 50; // 80
const serviceFee = calcPercent(totalBalance, servicePercent) + 10;
const tokenFee = calcPercent(totalBalance, tokenPercent) + 10;
const tranferResult = await mainBalance.sendTransferServiceFee(owner.getSender(), toNano(serviceFee), toNano(tokenFee));
expect(tranferResult.transactions).toHaveTransaction({
from: owner.address,
to: mainBalance.address,
success: false,
exitCode: contractConfig.error.FUNDS_EXCEED
});
});
it("should withdraw user coins", async () => {
await mainBalance.sendDeposit(user.getSender(), toNano(20));
await mainBalance.sendDeposit(user.getSender(), toNano(40));
await mainBalance.sendDeposit(anotherUser.getSender(), toNano(90));
let totalBalance = 20 + 40 + 90; // 150
const serviceFee = calcPercent(totalBalance, servicePercent);
const tokenFee = calcPercent(totalBalance, tokenPercent);
await mainBalance.sendTransferServiceFee(owner.getSender(), toNano(serviceFee), toNano(tokenFee));
let userWithdrawAmount = toNano(60 - calcPercent(60, contractConfig.totalFee)); // data from backend, - 10% fee
const withdrawResult = await mainBalance.sendWithdraw(owner.getSender(), user.address, userWithdrawAmount);
expect(withdrawResult.transactions).toHaveTransaction({
from: mainBalance.address,
to: user.address,
value: userWithdrawAmount,
success: true,
});
expect(+fromNano(await mainBalance.getDepositFunds())).toBe(0);
console.log("smc balance after transfer fee and withdraw", fromNano(await mainBalance.getBalance()));
let anotherUserWithdrawAmount = toNano(90 - calcPercent(90, contractConfig.totalFee)); // data from backend, - 10% fee
const secondWithdrawResult = await mainBalance.sendWithdraw(owner.getSender(), anotherUser.address, anotherUserWithdrawAmount);
expect(secondWithdrawResult.transactions).toHaveTransaction({
from: mainBalance.address,
to: anotherUser.address,
value: anotherUserWithdrawAmount,
success: true,
});
console.log("smc balance after another withdraw", fromNano(await mainBalance.getBalance()));
});
});

13
tsconfig.json Normal file
View File

@ -0,0 +1,13 @@
{
"compilerOptions": {
"target": "ES2020",
"outDir": "dist",
"module": "commonjs",
"declaration": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true,
"resolveJsonModule": true
}
}

View File

@ -0,0 +1,6 @@
import { CompilerConfig } from '@ton/blueprint';
export const compile: CompilerConfig = {
lang: 'func',
targets: ['contracts/main_balance.fc'],
};

130
wrappers/MainBalance.ts Normal file
View File

@ -0,0 +1,130 @@
import { Address, beginCell, Cell, Contract, contractAddress, ContractProvider, Sender, SendMode, toNano } from '@ton/core';
import contractConfig from '../config/contract.config';
export type MainBalanceConfig = {
ownerAddress: string,
depositFunds: bigint | 0,
serviceAddress: string,
tokenAddress: string
};
export function mainBalanceConfigToCell(config: MainBalanceConfig): Cell {
return beginCell()
.storeAddress(Address.parse(config.ownerAddress))
.storeCoins(config.depositFunds)
.storeAddress(Address.parse(config.serviceAddress))
.storeAddress(Address.parse(config.tokenAddress))
.endCell();
}
export class MainBalance implements Contract {
constructor(readonly address: Address, readonly init?: { code: Cell; data: Cell }) {}
static createFromAddress(address: Address) {
return new MainBalance(address);
}
static createFromConfig(config: MainBalanceConfig, code: Cell, workchain = 0) {
const data = mainBalanceConfigToCell(config);
const init = { code, data };
return new MainBalance(contractAddress(workchain, init), init);
}
async sendDeploy(provider: ContractProvider, via: Sender, value: bigint) {
await provider.internal(via, {
value,
sendMode: SendMode.PAY_GAS_SEPARATELY,
body: beginCell().endCell(),
});
}
async sendDeposit(provider: ContractProvider, via: Sender, depositAmount: bigint) {
await provider.internal(via, {
value: depositAmount,
sendMode: SendMode.PAY_GAS_SEPARATELY,
body: beginCell()
.storeUint(contractConfig.op.DEPOSIT, 32)
.endCell(),
});
}
async sendChangeOwnerAddress(provider: ContractProvider, via: Sender, newOwnerAddress: Address) {
await provider.internal(via, {
value: toNano('0.01'),
sendMode: SendMode.PAY_GAS_SEPARATELY,
body: beginCell()
.storeUint(contractConfig.op.CHANGE_OWNER_ADDRESS, 32)
.storeAddress(newOwnerAddress)
.endCell(),
});
}
async sendChangeServiceAddress(
provider: ContractProvider,
via: Sender,
newServiceAddress: Address,
newTokenAddress: Address
) {
await provider.internal(via, {
value: toNano('0.005'),
sendMode: SendMode.PAY_GAS_SEPARATELY,
body: beginCell()
.storeUint(contractConfig.op.CHANGE_SERVICE_ADDRESS, 32)
.storeAddress(newServiceAddress)
.storeAddress(newTokenAddress)
.endCell()
});
}
async sendTransferServiceFee(provider: ContractProvider, via: Sender, serviceCoins: bigint, liquidityCoins: bigint) {
await provider.internal(via, {
value: toNano('0.005'),
sendMode: SendMode.PAY_GAS_SEPARATELY,
body: beginCell()
.storeUint(contractConfig.op.TRANSFER_SERVICE_FEE, 32)
.storeCoins(serviceCoins)
.storeCoins(liquidityCoins)
.endCell(),
});
}
async sendWithdraw(provider: ContractProvider, via: Sender, userAddress: Address, userCoins: bigint) {
await provider.internal(via, {
value: toNano('0.005'),
sendMode: SendMode.PAY_GAS_SEPARATELY,
body: beginCell()
.storeUint(contractConfig.op.WITHDRAW, 32)
.storeAddress(userAddress)
.storeCoins(userCoins)
.endCell(),
});
}
async getBalance(provider: ContractProvider) : Promise<number> {
const { stack } = await provider.get('get_smc_balance', []);
return stack.readNumber();
}
async getOwnerAddress(provider: ContractProvider) : Promise<Address> {
const { stack } = await provider.get('get_storage_data', []);
return stack.readAddress();
}
async getDepositFunds(provider: ContractProvider) : Promise<number> {
const { stack } = await provider.get('get_storage_data', []);
stack.readAddress();
return stack.readNumber();
}
async getServiceAddress(provider: ContractProvider) : Promise<any> {
const { stack } = await provider.get('get_storage_data', []);
stack.readAddress();
stack.readNumber();
return {
serviceAddr: stack.readAddress(),
tokenAddr: stack.readAddress()
}
}
}