Skip to content

Commit

Permalink
initial release
Browse files Browse the repository at this point in the history
  • Loading branch information
dariotarantini committed Jan 25, 2023
0 parents commit b124d4e
Show file tree
Hide file tree
Showing 59 changed files with 6,432 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
DEPLOYER_MNEMONIC=
ADMIN_ADDRESS=
# API_KEY=
TESTNET=yes
# MAINNET=no
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
build/*.fif
build/*.cell
node_modules
.env*
!.env.example
.wget-hsts
bin
.vs_code
/.idea
.DS_Store
674 changes: 674 additions & 0 deletions LICENSE

Large diffs are not rendered by default.

34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# STON.fi Decentralized Exchange
[![TON](https://img.shields.io/badge/based%20on-TON-blue)](https://ton.org/)
[![License](https://img.shields.io/badge/license-GPL--3.0-brightgreen)](https://opensource.org/licenses/GPL-3.0)

Core contracts for the STON.fi DEX protocol.

## Contract deployments
The current version of DEX contracts are deployed at the following addresses:
- `EQB3ncyBUTjZUA5EnFKR5_EnOMI9V1tTEAAPaiU71gc4TiUt` for [mainnet](https://tonscan.org/address/EQB3ncyBUTjZUA5EnFKR5_EnOMI9V1tTEAAPaiU71gc4TiUt)
- `EQBsGx9ArADUrREB34W-ghgsCgBShvfUr4Jvlu-0KGc33Rbt` for [testnet](https://testnet.tonscan.org/address/EQBsGx9ArADUrREB34W-ghgsCgBShvfUr4Jvlu-0KGc33Rbt).

It was built from tag [v1.0.0](https://github.com/ston-fi/dex-core/releases/tag/v1.0.0).

## Local Development
The following assumes the use of `node@>=16`.

### Install Dependencies
`npm install`

### Compile Contracts
`npm run build`

### Run Tests
`npm run test`

### Deploy Contracts
`npm run deploy`

## Security
This repository is subject to STON.fi bug bounty program, see [here](https://github.com/ston-fi/bug-bounty) for more info.
Security vulnerabilities should be disclosed to the project maintainers by email to [email protected].

## Licensing
The license for STON.fi Decentralized Exchange is the GNU General Public License v3.0 (GPL-3.0), see [LICENSE](LICENSE).
41 changes: 41 additions & 0 deletions build/_build.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import fs from "fs";
import path from "path";
import glob from "fast-glob";
import { compileContract } from "ton-compiler";

async function main() {
console.log(`=================================================================`);
console.log(`Build script running, let's find some FunC contracts to compile..`);

// go over all the root contracts in the contracts directory
const rootContracts = glob.sync(["contracts/*.fc", "contracts/*.func"]);

for (const rootContract of rootContracts) {
const contractName = path.parse(rootContract).name;
if (contractName == "common") break;
console.log(`\n* Found contract '${contractName}' - let's compile it:`);

let result = await compileContract({
files: [rootContract],
});

if (!result.ok) {
console.error(`\n* Compilation failed!`);
console.error(result.log);
return;
}

console.log(` - Deleting old build artifact...`);
glob.sync([`build/${contractName}.cell`, `build/${contractName}.fif`]).map((f) => {
fs.unlinkSync(f);
});

let fiftCellSource = '"Asm.fif" include\n' + result.fift + "\n";
fs.writeFileSync(`build/${contractName}.fif`, fiftCellSource.replace(/\\n/g, "\n"), "utf8");
fs.writeFileSync(`build/${contractName}.cell`, result.output as Buffer);
}

console.log(``);
}

main();
136 changes: 136 additions & 0 deletions build/_deploy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import axios from "axios";
import axiosThrottle from "axios-request-throttle";
axiosThrottle.use(axios, { requestsPerSecond: 0.5 });

import dotenv from "dotenv";
dotenv.config();

import fs from "fs";
import path from "path";
import glob from "fast-glob";
import { Address, Cell, CellMessage, CommonMessageInfo, fromNano, InternalMessage, StateInit, toNano } from "ton";
import { TonClient, WalletContract, WalletV3R2Source, contractAddress, SendMode } from "ton";
import { mnemonicNew, mnemonicToWalletKey } from "ton-crypto";

async function main() {
console.log(`=================================================================`);
console.log(`Deploy script running, let's find some contracts to deploy..`);

let chain: string,
endpointUrl: string;
if (process.env.TESTNET || process.env.npm_lifecycle_event == "deploy:testnet") {
console.log(`\n* We are working with 'testnet' (https://t.me/testgiver_ton_bot will give you testnet TON)`);
endpointUrl = "https://testnet.toncenter.com/api/v2/jsonRPC";
} else {
console.log(`\n* We are working with 'mainnet'`);
endpointUrl = "https://mainnet.tonhubapi.com/jsonRPC";
}

// initialize globals
const client = new TonClient({ endpoint: endpointUrl, apiKey: process.env.API_KEY });
const newContractFunding = toNano(0.1); // this will be (almost in full) the balance of a new deployed contract and allow it to pay rent
const workchain = 0;

const deployConfigEnv = ".env";
let deployerMnemonic;
if (!process.env.DEPLOYER_MNEMONIC) {
console.log(` - ERROR: No DEPLOYER_MNEMONIC env variable found, please add it to env`);
process.exit(1);
} else {
console.log(`\n* Config file '${deployConfigEnv}' found and will be used for deployment!`);
deployerMnemonic = process.env.DEPLOYER_MNEMONIC;
}

// open the wallet and make sure it has enough TON
const walletKey = await mnemonicToWalletKey(deployerMnemonic.split(" "));
const walletContract = WalletContract.create(client, WalletV3R2Source.create({ publicKey: walletKey.publicKey, workchain }));
console.log(` - Wallet address used to deploy from is: ${walletContract.address.toFriendly()}`);
const walletBalance = await client.getBalance(walletContract.address);
await sleep(3 * 1000);
if (walletBalance.lt(toNano(0.2))) {
console.log(` - ERROR: Wallet has less than 0.2 TON for gas (${fromNano(walletBalance)} TON), please send some TON for gas first`);
process.exit(1);
} else {
console.log(` - Wallet balance is ${fromNano(walletBalance)} TON, which will be used for gas`);
}

const rootContracts = glob.sync(["build/*.deploy.ts"]);
for (const rootContract of rootContracts) {
console.log(`\n* Found root contract '${rootContract} - let's deploy it':`);
const contractName = path.parse(path.parse(rootContract).name).name;

const deployInitScript = require(__dirname + "/../" + rootContract);
if (typeof deployInitScript.initData !== "function") {
console.log(` - ERROR: '${rootContract}' does not have 'initData()' function`);
process.exit(1);
}
const initDataCell = deployInitScript.initData() as Cell;

if (typeof deployInitScript.initMessage !== "function") {
console.log(` - ERROR: '${rootContract}' does not have 'initMessage()' function`);
process.exit(1);
}
const initMessageCell = deployInitScript.initMessage() as Cell | null;

const cellArtifact = `build/${contractName}.cell`;
if (!fs.existsSync(cellArtifact)) {
console.log(` - ERROR: '${cellArtifact}' not found, did you build?`);
process.exit(1);
}
const initCodeCell = Cell.fromBoc(fs.readFileSync(cellArtifact))[0];

const newContractAddress = contractAddress({ workchain, initialData: initDataCell, initialCode: initCodeCell });
console.log(` - Based on your init code+data, your new contract address is: ${newContractAddress.toFriendly()}`);
if (await client.isContractDeployed(newContractAddress)) {
console.log(` - Looks like the contract is already deployed in this address, skipping deployment`);
continue;
}
await sleep(2000);

console.log(` - Let's deploy the contract on-chain..`);
const seqno = await walletContract.getSeqNo();
await sleep(2000);

const transfer = walletContract.createTransfer({
secretKey: walletKey.secretKey,
seqno: seqno,
sendMode: SendMode.PAY_GAS_SEPARATLY + SendMode.IGNORE_ERRORS,
order: new InternalMessage({
to: newContractAddress,
value: newContractFunding,
bounce: false,
body: new CommonMessageInfo({
stateInit: new StateInit({ data: initDataCell, code: initCodeCell }),
body: initMessageCell !== null ? new CellMessage(initMessageCell) : null,
}),
}),
});
await client.sendExternalMessage(walletContract, transfer);
await sleep(1000);
console.log(` - Deploy transaction sent successfully`);

console.log(` - Waiting up to 75 seconds to check if the contract was actually deployed..`);
for (let attempt = 0; attempt < 30; attempt++) {
await sleep(2500);
const seqnoAfter = await walletContract.getSeqNo();
if (seqnoAfter > seqno) break;
}
await sleep(5 * 1000);
if (await client.isContractDeployed(newContractAddress)) {
console.log(` - SUCCESS! Contract deployed successfully to address: ${newContractAddress.toFriendly()}`);
await sleep(1000);
const contractBalance = await client.getBalance(newContractAddress);
console.log(` - New contract balance is now ${fromNano(contractBalance)} TON, make sure it has enough to pay rent`);
} else {
console.log(` - FAILURE! Contract address still looks uninitialized: ${newContractAddress.toFriendly()}`);
}
}

console.log(``);
}

main();

function sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
22 changes: 22 additions & 0 deletions build/router.deploy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import * as router from "../contracts/router";
import fs from "fs";
import { Address, TupleSlice, WalletContract, Cell, beginCell } from "ton";
import dotenv from "dotenv";
dotenv.config();

export function initData() {
if (process.env.ADMIN_ADDRESS === undefined)
throw new Error("ADMIN_ADDRESS is not defined");

return router.data({
isLocked: false,
adminAddress: Address.parseFriendly(process.env.ADMIN_ADDRESS).address,
LPWalletCode: Cell.fromBoc(fs.readFileSync("build/lp_wallet.cell"))[0],
poolCode: Cell.fromBoc(fs.readFileSync("build/pool.cell"))[0],
LPAccountCode: Cell.fromBoc(fs.readFileSync("build/lp_account.cell"))[0],
});
}

export function initMessage() {
return null;
}
42 changes: 42 additions & 0 deletions contracts/common/gas.func
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
cell get_gas_config_param(int wc) inline {
if (wc == -1) {
return config_param(20);
} else {
return config_param(21);
}
}

(slice, (int, int)) load_gas_flat_pfx(slice param) inline {
var flat_gas_limit = param~load_uint(64);
var flat_gas_price = param~load_uint(64);
return (param, (flat_gas_limit, flat_gas_price));
}

(slice, int) load_gas_prices(slice param) inline {
var gas_price = param~load_uint(64);
return (param, gas_price);
}

(slice, (int, int, int)) load_gas_limits_prices(slice param) inline_ref {
var contructor_tag = param~load_uint(8);
if (contructor_tag == 0xd1) {
var (flat_gas_limit, flat_gas_price) = param~load_gas_flat_pfx();
var (_, _, gas_price) = param~load_gas_limits_prices();
return (param, (flat_gas_limit, flat_gas_price, gas_price));
} elseif ((contructor_tag == 0xde) | (contructor_tag == 0xdd)) {
var gas_price = param~load_gas_prices();
return (param, (0, 0, gas_price));
} else {
return (param, (0, 0, 0));
}
}

(int, int, int) get_gas_limits_prices(int wc) inline {
var gas_price_config = get_gas_config_param(wc).begin_parse();
return gas_price_config~load_gas_limits_prices();
}

int get_gas_fee(int gas_amount, int wc) inline_ref {
var (flat_gas_limit, flat_gas_price, gas_price) = get_gas_limits_prices(wc);
return gas_amount < flat_gas_limit ? flat_gas_price : (gas_amount - flat_gas_limit) * (gas_price >> 16) + flat_gas_price;
}
11 changes: 11 additions & 0 deletions contracts/common/jetton-utils.func
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
(builder) create_simple_transfer_body(int query_id, int fwd_amount, int jetton_amount, slice to) inline {
return begin_cell()
.store_uint(0xf8a7ea5, 32)
.store_uint(query_id, 64)
.store_coins(jetton_amount)
.store_slice(to) ;; to_owner_address
.store_slice(to)
.store_uint(0, 1)
.store_coins(fwd_amount) ;; forward_ton_amount
.store_uint(0, 1);
}
51 changes: 51 additions & 0 deletions contracts/common/messages.func
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
const NORMAL = 0;
const PAID_EXTERNALLY = 1;
const IGNORE_ERRORS = 2;

const DESTROY_IF_ZERO = 32;
const CARRY_REMAINING_GAS = 64;
const CARRY_ALL_BALANCE = 128;

() send_empty_message(int amount, slice to, int mode) impure inline_ref {
cell msg = begin_cell()
.store_uint(0x18, 6)
.store_slice(to)
.store_coins(amount)
.store_uint(0, 107)
.end_cell();
send_raw_message(msg, mode);
}

() send_simple_message(int amount, slice to, cell body, int mode) impure inline_ref {
cell msg = begin_cell()
.store_uint(0x18, 6)
.store_slice(to)
.store_coins(amount)
.store_uint(1, 107)
.store_ref(body)
.end_cell();
send_raw_message(msg, mode);
}

() send_message_nobounce(int amount, slice to, cell body, int mode) impure inline_ref {
cell msg = begin_cell()
.store_uint(0x10, 6)
.store_slice(to)
.store_coins(amount)
.store_uint(1, 107)
.store_ref(body)
.end_cell();
send_raw_message(msg, mode);
}

() send_message_with_stateinit(int amount, slice to, cell state_init, cell body, int mode) impure inline_ref {
cell msg = begin_cell()
.store_uint(0x18, 6)
.store_slice(to)
.store_coins(amount)
.store_uint(7, 108)
.store_ref(state_init)
.store_ref(body)
.end_cell();
send_raw_message(msg, mode);
}
5 changes: 5 additions & 0 deletions contracts/common/stdlib.func
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
;; sdlib++
(int) equal_slices (slice s1, slice s2) asm "SDEQ";
(int) mod (int x, int y) asm "MOD";
(int) divc (int x, int y) asm "DIVC";
slice preload_bits_offset(slice s, int offset, int len) asm "SDSUBSTR";
22 changes: 22 additions & 0 deletions contracts/common/utils.func
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
const FWD_GAS = 35000000; ;; 0.04 TON

() send_payload(slice caller, cell payload) impure inline_ref {
cell msg = begin_cell()
.store_uint(0x18, 6)
.store_slice(caller)
.store_coins(0)
.store_uint(1, 1 + 4 + 4 + 64 + 32 + 1 + 1)
.store_ref(payload)
.end_cell();
send_raw_message(msg, 128); ;; CARRY_ALL_BALANCE
}

(int) get_workchain(slice address) inline {
(int wc, _) = parse_std_addr(address);
return wc;
}

() force_chain(int workchain, slice address, int error_code) impure inline {
(int wc) = get_workchain(address);
throw_unless(error_code, wc == workchain);
}
Loading

0 comments on commit b124d4e

Please sign in to comment.