-
Notifications
You must be signed in to change notification settings - Fork 82
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit b124d4e
Showing
59 changed files
with
6,432 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
DEPLOYER_MNEMONIC= | ||
ADMIN_ADDRESS= | ||
# API_KEY= | ||
TESTNET=yes | ||
# MAINNET=no |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |
Oops, something went wrong.