Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions jetton_tests/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*.pk
build
59 changes: 59 additions & 0 deletions jetton_tests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Jetton Minter example project

This project allows you to:

1. Build basic jetton minter contract
2. Aims to *hopefully* test any nftcollection contract for compliance with [Jetton standerd](https://github.com/ton-blockchain/TIPs/issues/74)
3. Deploy minter contract via `toncli deploy`
4. Manually deploy jetton wallet via minting tokens
5. Manually send to other jetton wallets
6. Manyally burn coins on your wallet

## Building

`toncli start jetton_minter`
`toncli build`

## Testing

Same here `toncli run_test`
If you encounter **error 6** during *run_tests*
make shure that your binaries are built according to:[this manual](https://github.com/disintar/toncli/blob/master/docs/advanced/func_tests_new.md)

## Deploying minter contract

This project consists of two sub-projects **jetton_minter** and **jetton_wallet**
You can see that in the *project.yml*
**BOTH** of those have to be built.
First type:`toncli build`
However it makes sense to deploy only *jetton_minter*.
Prior to deployment you need to check out *fift/minter_data.fif*
and change all mock configuration values to your own liking.
To deploy run:`toncli deploy -n testnet jetton_minter`.

## Minting jettons

To mint coins to your wallet
you will have to:

+ Configure *fift/mint_jettons.fif* script with your own values:
[Take a look](https://github.com/ton-blockchain/TIPs/issues/74)

+ Make yourself familiar with process of sending [internal messages](https://github.com/disintar/toncli/blob/master/docs/advanced/send_fift_internal.md)

`toncli send -n testnet -a 0.035 -c jetton_minter --body fift/mint_jettons.fif`

## Sending jettons

To send coins to someone elses jetton wallet
you will have to:

+ Setup values in *fift/send_jettons.fif*
+ Run:`toncli send -n testnet -a 0.1 --address < your jetton wallet addr> --body fift/send_jettons.fif`

## Burning jettons

To burn jettons

+ Setup values in *fift/burn_jettons.fif*
+ Run `toncli send -n testnet -a 0.1 --address < your jetton wallet addr > --body fift/burn_jettons.fif`
Empty file added jetton_tests/fift/.gitkeep
Empty file.
8 changes: 8 additions & 0 deletions jetton_tests/fift/burn_jettons.fif
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
"TonUtil.fif" include

<b
0x595f07bc 32 u, // op::burn
12345 64 u, // query_id
5000000000 Gram, // Burning 5 jettons
0 3 u, // addr_none for resp_dst and no custom payload
b>
33 changes: 33 additions & 0 deletions jetton_tests/fift/mint_jettons.fif
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
"TonUtil.fif" include

"EQDlT07NpSh0uj-aSBkF2TRxOqR2nw0ErOQsA6TYakr1-FxP" constant mint_address
0x178d4519 constant internal_transfer
1000000000 10 * constant mint_amount
30000000 constant forward_amount

mint_address
$>smca 0= abort"Specify valid mint addr"
drop // Drop flags

2constant mint_raw // worchain and addr into single constant

// Here goes master message
// internal_transfer

<b
internal_transfer 32 u, // op internal_transfer
4321 64 u, // query_id
mint_amount Gram, // 10 jettons
mint_raw Addr, // From addr
0 2 u, // addr_none for simplicity Address vwhere transfer notification would be sent
0 Gram, // No address no forward
0 1 u, // normal forward payload
b>

<b
21 32 u, // op mint
1234 64 u, // query_id
mint_raw Addr,
forward_amount Gram,
swap ref, // master message ref
b>
28 changes: 28 additions & 0 deletions jetton_tests/fift/minter_data.fif
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"TonUtil.fif" include
"Asm.fif" include


"EQDlT07NpSh0uj-aSBkF2TRxOqR2nw0ErOQsA6TYakr1-FxP" constant owner_address // Specify your own
"https://raw.githubusercontent.com/Trinketer22/token-contract/main/ft/web-example/test_jetton.json" constant jetton_meta // Specify your own
"build/jetton_wallet.fif" constant wallet_code_path
1000000000 100 * constant jetton_supply // Starting jetton supply

<b
1 8 u, // Off-chain token
jetton_meta
$>B B,
b>


owner_address
$>smca 0= abort"Specify valid admin addr"
drop // Drop flags

<b
jetton_supply Gram, // total supply
-rot Addr, // admin address
swap ref, // content cell
wallet_code_path include // Loading jetton code
over tuple? { nip } if // In testing mode (asm-mode 3) include returns ( cell, tuple ) we don't want tuple in the cell
ref, // code cell
b>
27 changes: 27 additions & 0 deletions jetton_tests/fift/send_jettons.fif
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"TonUtil.fif" include

"EQDRebAnF1pvH1YsKNp7mtpsz+CLs6WxaffUojt1ijyrazkg" constant receiver_address
1000000000 constant send_amount
20000000 constant forward_amount
12345 constant query_id
0xf8a7ea5 constant op_transfer

receiver_address
$>smca 0= abort"Specify valid send addr"
drop // Drop flags

2constant send_addr // worchain and addr into single constant
<b
op_transfer 32 u, // op transfer
query_id 64 u, // query_id
send_amount Gram, // Sending one jetton
send_addr Addr,
0 2 u, // addr_none for resp_dst
0 1 u, // No custom payload
forward_amount Gram, // forward ton amount 0.03
0 1 u, // no ref forward_payload
b>
// Here goes master message
// internal_transfer


30 changes: 30 additions & 0 deletions jetton_tests/fift/wallet_data.fif
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"TonUtil.fif" include
"Asm.fif" include

1000000000 10 * constant initial_balance // Wallet balance
"build/jetton_wallet.fif" constant wallet_code

/*
This file is primerely for tests to run
Main way of deploying jetton wallet is via jetton minter "mint" operation.
Sending jettons from already deployed wallet is also an option
Thus random 256 bit value will be used ans and address for collection and owner

We're going to use newkeypair as a way to generate two random 256 bit values
Do not do that in real app
*/

newkeypair

256 B>u@
swap
256 B>u@

<b
initial_balance Gram, // 10 jettons balance ( if decimals is set to 9 )
Basechain rot Addr, // owner
Basechain rot Addr, // minter addr
wallet_code include
over tuple? { nip } if // In testing mode include will produce tuple with method names we don't want that in the cell
ref, // wallet code
b>
38 changes: 38 additions & 0 deletions jetton_tests/project.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
jetton_minter:
data: fift/minter_data.fif
func:
- ../ft/op-codes.fc
- ../ft/params.fc
- ../ft/jetton-utils.fc
- ../ft/discovery-params.fc
- tests/utils/helpers.func
- ../ft/jetton-minter.fc
tests:
- tests/minter-tests.func
- tests/minter-tests-int.func

jetton_discoverable:
data: fift/minter_data.fif
func:
- ../ft/op-codes.fc
- ../ft/params.fc
- tests/utils/helpers.func
- ../ft/jetton-utils.fc
- ../ft/discovery-params.fc
- ../ft/jetton-minter-discoverable.fc
tests:
- tests/minter-tests.func
- tests/minter-tests-int.func


jetton_wallet:
data: fift/wallet_data.fif
func:
- ../ft/op-codes.fc
- ../ft/params.fc
- ../ft/jetton-utils.fc
- tests/utils/helpers.func
- ../ft/jetton-wallet.fc
tests:
- tests/wallet-tests.func
- tests/wallet-tests-int.func
Empty file added jetton_tests/tests/.gitkeep
Empty file.
96 changes: 96 additions & 0 deletions jetton_tests/tests/minter-tests-int.func
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#pragma version >=0.2.0;

#include "utils/constants.func";


int __test_get_jetton_data_mint() {

var( gas_before, stack ) = invoke_method( get_jetton_data, [] );

int total_supply = stack.first();
int mintable? = stack.second();
slice admin = stack.third();
cell content = stack.fourth();
cell code = stack.at( 4 );


;; Can't test non mintable contract with mint message.
if( ~ mintable? ) {
return gas_before;
}

int query_id = rand(1337) + 1;
int forward_ton = one_unit / 10;
int mint_amount = ( rand( 100 ) + 1 ) * one_unit / 10;
slice rand_addr = generate_internal_address_with_custom_data(0, 0, random());
slice mint_dst = generate_internal_address_with_custom_data(0, 0, random());
slice mint_from = generate_empty_address();
cell mint_payload = generate_jetton_internal_transfer_request( query_id, mint_amount, mint_from, rand_addr, forward_ton, null(), false ).end_cell();
builder mint_body = generate_internal_message_body( op_mint, query_id ).store_slice( mint_dst ).store_grams( forward_ton ).store_ref( mint_payload );
;; Testing mint with non-admin address
cell msg = generate_internal_message_custom( 0, 0, 0, mint_body, admin, null(), 0 );

var( gas_mint, _ ) = invoke_method( recv_internal, [ one_unit, one_unit, msg, mint_body.end_cell().begin_parse() ] );
var( gas_after, stack ) = invoke_method( get_jetton_data, [] );
;; Total supply should increase by our mint_amount
throw_unless( 500, total_supply + mint_amount == stack.first() );

;; Rest should not change

throw_unless( 501, mintable? == stack.second() );
throw_unless( 502, equal_slices( admin, stack.third() ) );
throw_unless( 503, equal_slices( content.begin_parse(), stack.fourth().begin_parse() ) );
throw_unless( 504, equal_slices( code.begin_parse(), stack.at( 4 ).begin_parse() ) );


return gas_before + gas_mint + gas_after;
}

int __test_get_jetton_data_burn() {

var( gas_before, stack ) = invoke_method( get_jetton_data, [] );

int total_supply = stack.first();
int mintable? = stack.second();
slice admin = stack.third();
cell content = stack.fourth();
cell code = stack.at( 4 );

;;Nothing to burn
if( total_supply == 0 ) {
return gas_before;
}

{-
If jetton is not mintabe, does it mean that it's necesserely
non-burnable too? IDK.
I'd say doesn't and burnable? flag required.
So we would ignore mintable? in this test.
-}

int query_id = rand(1337) + 1;
int forward_ton = one_unit / 10;
int burn_amount = ( rand( 100 ) + 1 ) * one_unit / 10;

slice jetton_addr = my_address(); ;;In testing mode my_address() for test code equals my_address() for contract code
slice sender = generate_internal_address_with_custom_data( 0, 0, random() );
slice src = calculate_user_jetton_wallet_address( sender, jetton_addr, code );
var msg_body = generate_jetton_burn_notification( query_id, burn_amount, sender, admin );
cell msg = generate_internal_message_custom( 0, 0, 0, msg_body, src, null(), 0);

( int gas_burn, _ ) = invoke_method( recv_internal, [one_unit, one_unit, msg, msg_body.end_cell().begin_parse() ] );

( int gas_after, stack ) = invoke_method( get_jetton_data, [] );


throw_unless( 500, total_supply - burn_amount == stack.first() );

;; Rest should not change

throw_unless( 501, mintable? == stack.second() );
throw_unless( 502, equal_slices( admin, stack.third() ) );
throw_unless( 503, equal_slices( content.begin_parse(), stack.fourth().begin_parse() ) );
throw_unless( 504, equal_slices( code.begin_parse(), stack.at( 4 ).begin_parse() ) );

return gas_before + gas_burn + gas_after;
}
Loading