smc upload
This commit is contained in:
parent
3bb5c30b2b
commit
e3acca0523
140
.gitignore
vendored
140
.gitignore
vendored
|
@ -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
1
.prettierignore
Normal file
|
@ -0,0 +1 @@
|
|||
build
|
7
.prettierrc
Normal file
7
.prettierrc
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"printWidth": 120,
|
||||
"tabWidth": 4,
|
||||
"singleQuote": true,
|
||||
"bracketSpacing": true,
|
||||
"semi": true
|
||||
}
|
16
README.md
16
README.md
|
@ -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`
|
1
build/MainBalance.compiled.json
Normal file
1
build/MainBalance.compiled.json
Normal file
|
@ -0,0 +1 @@
|
|||
{"hash":"30afc256a9ab06895264b563ce9c62abad61405d10ac88154956869e98c90fc5","hashBase64":"MK/CVqmrBolSZLVjzpxiq61hQF0QrIgVSVaGnpjJD8U=","hex":"b5ee9c7241021201000210000114ff00f4a413f4bcf2c80b0102016202090202ce030803f743322c700925f03e0d0d3030171b0925f03e0fa403002d31fed44d0fa40fa00fa40fa403025c0018e1c343435037aa90414a04013c85004cf1658fa0201cf1601cf16c9ed54e03624c0028e21345055c705f2e193fa403020f0024313c85004cf1658fa0201cf1601cf16c9ed54e024c003e30224c004e3025b33018040507005230335144c705f2e193fa403020d749c202f2e19420f00258c85004cf1658fa0201cf1601cf16c9ed5401fa345151c705f2e19324c000f2d19101fa00fa00305ca05006bcf2d19252208bb53657276696365206665658708018c8cb055004cf1658fa027001cb6a12cb1f01cf16c971fb0052248d05541c9bdd9a5919481b1a5c5d5a591a5d1e4819995960708018c8cb055004cf1658fa027001cb6a12cb1f01cf16c971fb007002060022c85004cf1658fa0201cf1601cf16c9ed540086c0058e3859c705f2e193fa40fa003082080f424070fb028b857697468647261778708018c8cb055004cf1658fa027001cb6a12cb1f01cf16c971fb00e05f038417f2f000114fa4430c000f2e14d80201200a0f0201480b0c0021b67a1da89a1f481f401f481f48060d84500201200d0e0025b2017b51343e903e803e903e900c0408d7c0e0000fb0cbfe09dbc88c200202731011001dac40f6a2687d207d007d207d2018400021afddf6a2687d207d007d207d20182f81c010696ed8"}
|
37
config/contract.config.ts
Normal file
37
config/contract.config.ts
Normal 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
9
jest.config.ts
Normal 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
5984
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
24
package.json
Normal file
24
package.json
Normal 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"
|
||||
}
|
||||
}
|
13
scripts/1.deployMainBalance.ts
Normal file
13
scripts/1.deployMainBalance.ts
Normal 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);
|
||||
}
|
14
scripts/2.depositToMainBalance.ts
Normal file
14
scripts/2.depositToMainBalance.ts
Normal 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}`);
|
||||
}
|
15
scripts/3.changeOwnerAddress.ts
Normal file
15
scripts/3.changeOwnerAddress.ts
Normal 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}`);
|
||||
|
||||
}
|
20
scripts/4.changeServiceAddress.ts
Normal file
20
scripts/4.changeServiceAddress.ts
Normal 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}`);
|
||||
}
|
22
scripts/5.transferServiceFee.ts
Normal file
22
scripts/5.transferServiceFee.ts
Normal 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}`);
|
||||
}
|
18
scripts/6.withdrawToUser.ts
Normal file
18
scripts/6.withdrawToUser.ts
Normal 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
15
scripts/utils.ts
Normal 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
190
tests/MainBalance.spec.ts
Normal 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
13
tsconfig.json
Normal file
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"outDir": "dist",
|
||||
"module": "commonjs",
|
||||
"declaration": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"resolveJsonModule": true
|
||||
}
|
||||
}
|
6
wrappers/MainBalance.compile.ts
Normal file
6
wrappers/MainBalance.compile.ts
Normal 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
130
wrappers/MainBalance.ts
Normal 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()
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user