Skip to content
This repository has been archived by the owner on Jan 26, 2023. It is now read-only.

Tonstarter functionality #1

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 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
85 changes: 74 additions & 11 deletions sources/contract.spec.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
import { toNano } from 'ton';
import { createExecutorFromCode } from 'ton-nodejs';
import { toNano, Cell } from 'ton';
import { ContractExecutor, createExecutorFromCode } from 'ton-nodejs';
import { SampleTactContract, SampleTactContract_init } from './output/sample_SampleTactContract';
import { randomAddress } from './utils/randomAddress';
import BN from 'bn.js';

describe('contract', () => {
let owner = randomAddress(0, 'some-owner');
let nonOwner = randomAddress(0, 'some-non-owner');
let newOwner = randomAddress(0, 'new-owner');
let init: {
code: Cell
data: Cell
}
let executor: ContractExecutor;
let contract: SampleTactContract;

beforeEach(async () => {
init = await SampleTactContract_init(owner);
executor = await createExecutorFromCode({...init, balance: toNano(1)});
contract = new SampleTactContract(executor);
})

it('should deploy correctly', async () => {

// Create stateinit
let owner = randomAddress(0, 'some-owner');
let nonOwner = randomAddress(0, 'some-non-owner');
let init = await SampleTactContract_init(owner);
let executor = await createExecutorFromCode(init);
let contract = new SampleTactContract(executor);

// Check counter
expect((await contract.getCounter()).toString()).toEqual('0');

Expand All @@ -23,6 +32,60 @@ describe('contract', () => {
expect((await contract.getCounter()).toString()).toEqual('1');

// Non-owner
await expect(() => contract.send({ amount: toNano(1), from: nonOwner }, 'increment')).rejects.toThrowError('Constraints error');
await expect(() => contract.send({ amount: toNano(1), from: nonOwner }, 'increment')).rejects.toThrowError('Access denied');
});

it('should get meaning of life', async() => {
expect((await contract.getMeaningOfLife()).toString()).toEqual('42');
});

it('should increment counter', async() => {
await contract.send(
{ amount: toNano(1), from: owner },
{
$$type: "Add",
amount: new BN(13)
}
);
await contract.send({ amount: toNano(1), from: nonOwner }, 'dec');
expect((await contract.getCounter()).toString()).toEqual('12');
});

it('should deny changing owner', async() => {
await expect(contract.send(
{ amount: toNano(1), from: nonOwner },
{$$type: "ChangeOwner", newOwner}
)).rejects.toThrowError('Access denied');
});

it('should change owner', async() => {
await contract.send(
{ amount: toNano(1), from: owner },
{$$type: "ChangeOwner", newOwner}
)
expect((await contract.getOwner()).toFriendly()).toEqual(newOwner.toFriendly());
});

it('should withdraw', async() => {
await contract.send(
{ amount: toNano(1), from: owner },
{$$type: "Withdraw", amount: toNano(0.5)}
)

// TODO tx-emulator supports balances
});

it('should not be able to withdraw over balance', async() => {
await expect( contract.send(
{ amount: toNano(1), from: owner },
{$$type: "Withdraw", amount: toNano(1.5)}
)).rejects.toThrowError('Exit code: 478');
});

it('should not be able to withdraw from non-owner', async() => {
await expect( contract.send(
{ amount: toNano(1), from: nonOwner },
{$$type: "Withdraw", amount: toNano(0.5)}
)).rejects.toThrowError('Access denied');
});
});
36 changes: 30 additions & 6 deletions sources/contract.tact
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
import "@stdlib/ownable";

message Add {
amount: Int as uint32;
}

contract SampleTactContract {
message Withdraw {
amount: Int;
}

owner: Address;
contract SampleTactContract with OwnableTransferable {
counter: Int as uint32;
owner: Address;

init(owner: Address) {
self.owner = owner;
self.counter = 0;
}

fun add(v: Int) {

// Check sender
let ctx: Context = context();
require(ctx.sender == self.owner);
self.requireOwner();

// Update counter
self.counter = (self.counter + v);
Expand All @@ -26,11 +28,33 @@ contract SampleTactContract {
self.add(msg.amount);
}

receive(msg: Withdraw) {
self.requireOwner();
nativeThrowUnless(478, myBalance() > msg.amount);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we please use require here and provide text error?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do. I'm still testing things - changed to draft at the moment.

would you like to merge these changes to the template generally?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ofc

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

send(SendParameters{
to: self.owner,
value: msg.amount,
bounce: false
});
}

receive("increment") {
self.add(1);
}

receive("dec") {
self.counter = self.counter - 1;
}

get fun counter(): Int {
return self.counter;
}

get fun balance(): Int {
return myBalance();
}

get fun meaning_of_life(): Int {
return 42;
}
}
108 changes: 108 additions & 0 deletions sources/output/sample_SampleTactContract.abi
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,40 @@
}
}
},
{
"name": "ChangeOwner",
"header": 0,
"fields": [
{
"name": "newOwner",
"type": {
"kind": "ref",
"name": "Address",
"optional": false
}
}
],
"allocation": {
"prefix": 3067051791,
"root": {
"fields": [
{
"index": 0,
"size": {
"bits": 267,
"refs": 0
},
"kind": "address"
}
],
"next": null,
"size": {
"bits": 267,
"refs": 0
}
}
}
},
{
"name": "Add",
"header": 0,
Expand Down Expand Up @@ -311,6 +345,41 @@
}
}
}
},
{
"name": "Withdraw",
"header": 0,
"fields": [
{
"name": "amount",
"type": {
"kind": "ref",
"name": "Int",
"optional": false
}
}
],
"allocation": {
"prefix": 1286094280,
"root": {
"fields": [
{
"index": 0,
"size": {
"bits": 257,
"refs": 0
},
"kind": "int",
"bits": 257
}
],
"next": null,
"size": {
"bits": 257,
"refs": 0
}
}
}
}
],
"init": {
Expand All @@ -331,9 +400,21 @@
"kind": "internal-binary",
"type": "Add"
},
{
"kind": "internal-binary",
"type": "Withdraw"
},
{
"kind": "internal-comment",
"comment": "increment"
},
{
"kind": "internal-comment",
"comment": "dec"
},
{
"kind": "internal-binary",
"type": "ChangeOwner"
}
],
"getters": [
Expand All @@ -345,6 +426,33 @@
"name": "Int",
"optional": false
}
},
{
"name": "balance",
"args": [],
"returns": {
"kind": "ref",
"name": "Int",
"optional": false
}
},
{
"name": "meaning_of_life",
"args": [],
"returns": {
"kind": "ref",
"name": "Int",
"optional": false
}
},
{
"name": "owner",
"args": [],
"returns": {
"kind": "ref",
"name": "Address",
"optional": false
}
}
],
"dependsOn": {},
Expand Down
2 changes: 1 addition & 1 deletion sources/output/sample_SampleTactContract.abi.ipfs
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"version":"0.0.1","name":"SampleTactContract","structs":[{"name":"StateInit","header":0,"fields":[{"name":"code","type":{"kind":"ref","name":"Cell","optional":false}},{"name":"data","type":{"kind":"ref","name":"Cell","optional":false}}],"allocation":{"prefix":null,"root":{"fields":[{"index":0,"size":{"bits":0,"refs":1},"kind":"cell"},{"index":1,"size":{"bits":0,"refs":1},"kind":"cell"}],"next":null,"size":{"bits":0,"refs":2}}}},{"name":"Context","header":0,"fields":[{"name":"bounced","type":{"kind":"ref","name":"Bool","optional":false}},{"name":"sender","type":{"kind":"ref","name":"Address","optional":false}},{"name":"value","type":{"kind":"ref","name":"Int","optional":false}}],"allocation":{"prefix":null,"root":{"fields":[{"index":0,"size":{"bits":1,"refs":0},"kind":"int","bits":1},{"index":1,"size":{"bits":267,"refs":0},"kind":"address"},{"index":2,"size":{"bits":257,"refs":0},"kind":"int","bits":257}],"next":null,"size":{"bits":525,"refs":0}}}},{"name":"SendParameters","header":0,"fields":[{"name":"bounce","type":{"kind":"ref","name":"Bool","optional":false}},{"name":"to","type":{"kind":"ref","name":"Address","optional":false}},{"name":"value","type":{"kind":"ref","name":"Int","optional":false}},{"name":"mode","type":{"kind":"ref","name":"Int","optional":false}},{"name":"body","type":{"kind":"ref","name":"Cell","optional":true}},{"name":"code","type":{"kind":"ref","name":"Cell","optional":true}},{"name":"data","type":{"kind":"ref","name":"Cell","optional":true}}],"allocation":{"prefix":null,"root":{"fields":[{"index":0,"size":{"bits":1,"refs":0},"kind":"int","bits":1},{"index":1,"size":{"bits":267,"refs":0},"kind":"address"},{"index":2,"size":{"bits":257,"refs":0},"kind":"int","bits":257},{"index":3,"size":{"bits":257,"refs":0},"kind":"int","bits":257},{"index":4,"size":{"bits":1,"refs":1},"kind":"optional","inner":{"index":4,"size":{"bits":0,"refs":1},"kind":"cell"}},{"index":5,"size":{"bits":1,"refs":1},"kind":"optional","inner":{"index":5,"size":{"bits":0,"refs":1},"kind":"cell"}},{"index":6,"size":{"bits":1,"refs":1},"kind":"optional","inner":{"index":6,"size":{"bits":0,"refs":1},"kind":"cell"}}],"next":null,"size":{"bits":785,"refs":3}}}},{"name":"Add","header":0,"fields":[{"name":"amount","type":{"kind":"ref","name":"Int","optional":false}}],"allocation":{"prefix":3310826759,"root":{"fields":[{"index":0,"size":{"bits":32,"refs":0},"kind":"uint","bits":32}],"next":null,"size":{"bits":32,"refs":0}}}}],"init":{"name":"init_SampleTactContract","args":[{"name":"owner","type":{"kind":"ref","name":"Address","optional":false}}]},"receivers":[{"kind":"internal-binary","type":"Add"},{"kind":"internal-comment","comment":"increment"}],"getters":[{"name":"counter","args":[],"returns":{"kind":"ref","name":"Int","optional":false}}],"dependsOn":{},"errors":{"2":{"message":"Stack undeflow"},"3":{"message":"Stack overflow"},"4":{"message":"Integer overflow"},"5":{"message":"Integer out of expected range"},"6":{"message":"Invalid opcode"},"7":{"message":"Type check error"},"8":{"message":"Cell overflow"},"9":{"message":"Cell underflow"},"10":{"message":"Dictionary error"},"13":{"message":"Out of gas error"},"32":{"message":"Method ID not found"},"34":{"message":"Action is invalid or not supported"},"37":{"message":"Not enough TON"},"38":{"message":"Not enough extra-currencies"},"128":{"message":"Null reference exception"},"129":{"message":"Invalid serialization prefix"},"130":{"message":"Invalid incoming message"},"131":{"message":"Constraints error"},"132":{"message":"Access denied"},"133":{"message":"Contract stopped"},"134":{"message":"Invalid argument"}}}
{"version":"0.0.1","name":"SampleTactContract","structs":[{"name":"StateInit","header":0,"fields":[{"name":"code","type":{"kind":"ref","name":"Cell","optional":false}},{"name":"data","type":{"kind":"ref","name":"Cell","optional":false}}],"allocation":{"prefix":null,"root":{"fields":[{"index":0,"size":{"bits":0,"refs":1},"kind":"cell"},{"index":1,"size":{"bits":0,"refs":1},"kind":"cell"}],"next":null,"size":{"bits":0,"refs":2}}}},{"name":"Context","header":0,"fields":[{"name":"bounced","type":{"kind":"ref","name":"Bool","optional":false}},{"name":"sender","type":{"kind":"ref","name":"Address","optional":false}},{"name":"value","type":{"kind":"ref","name":"Int","optional":false}}],"allocation":{"prefix":null,"root":{"fields":[{"index":0,"size":{"bits":1,"refs":0},"kind":"int","bits":1},{"index":1,"size":{"bits":267,"refs":0},"kind":"address"},{"index":2,"size":{"bits":257,"refs":0},"kind":"int","bits":257}],"next":null,"size":{"bits":525,"refs":0}}}},{"name":"SendParameters","header":0,"fields":[{"name":"bounce","type":{"kind":"ref","name":"Bool","optional":false}},{"name":"to","type":{"kind":"ref","name":"Address","optional":false}},{"name":"value","type":{"kind":"ref","name":"Int","optional":false}},{"name":"mode","type":{"kind":"ref","name":"Int","optional":false}},{"name":"body","type":{"kind":"ref","name":"Cell","optional":true}},{"name":"code","type":{"kind":"ref","name":"Cell","optional":true}},{"name":"data","type":{"kind":"ref","name":"Cell","optional":true}}],"allocation":{"prefix":null,"root":{"fields":[{"index":0,"size":{"bits":1,"refs":0},"kind":"int","bits":1},{"index":1,"size":{"bits":267,"refs":0},"kind":"address"},{"index":2,"size":{"bits":257,"refs":0},"kind":"int","bits":257},{"index":3,"size":{"bits":257,"refs":0},"kind":"int","bits":257},{"index":4,"size":{"bits":1,"refs":1},"kind":"optional","inner":{"index":4,"size":{"bits":0,"refs":1},"kind":"cell"}},{"index":5,"size":{"bits":1,"refs":1},"kind":"optional","inner":{"index":5,"size":{"bits":0,"refs":1},"kind":"cell"}},{"index":6,"size":{"bits":1,"refs":1},"kind":"optional","inner":{"index":6,"size":{"bits":0,"refs":1},"kind":"cell"}}],"next":null,"size":{"bits":785,"refs":3}}}},{"name":"ChangeOwner","header":0,"fields":[{"name":"newOwner","type":{"kind":"ref","name":"Address","optional":false}}],"allocation":{"prefix":3067051791,"root":{"fields":[{"index":0,"size":{"bits":267,"refs":0},"kind":"address"}],"next":null,"size":{"bits":267,"refs":0}}}},{"name":"Add","header":0,"fields":[{"name":"amount","type":{"kind":"ref","name":"Int","optional":false}}],"allocation":{"prefix":3310826759,"root":{"fields":[{"index":0,"size":{"bits":32,"refs":0},"kind":"uint","bits":32}],"next":null,"size":{"bits":32,"refs":0}}}},{"name":"Withdraw","header":0,"fields":[{"name":"amount","type":{"kind":"ref","name":"Int","optional":false}}],"allocation":{"prefix":1286094280,"root":{"fields":[{"index":0,"size":{"bits":257,"refs":0},"kind":"int","bits":257}],"next":null,"size":{"bits":257,"refs":0}}}}],"init":{"name":"init_SampleTactContract","args":[{"name":"owner","type":{"kind":"ref","name":"Address","optional":false}}]},"receivers":[{"kind":"internal-binary","type":"Add"},{"kind":"internal-binary","type":"Withdraw"},{"kind":"internal-comment","comment":"increment"},{"kind":"internal-comment","comment":"dec"},{"kind":"internal-binary","type":"ChangeOwner"}],"getters":[{"name":"counter","args":[],"returns":{"kind":"ref","name":"Int","optional":false}},{"name":"balance","args":[],"returns":{"kind":"ref","name":"Int","optional":false}},{"name":"meaning_of_life","args":[],"returns":{"kind":"ref","name":"Int","optional":false}},{"name":"owner","args":[],"returns":{"kind":"ref","name":"Address","optional":false}}],"dependsOn":{},"errors":{"2":{"message":"Stack undeflow"},"3":{"message":"Stack overflow"},"4":{"message":"Integer overflow"},"5":{"message":"Integer out of expected range"},"6":{"message":"Invalid opcode"},"7":{"message":"Type check error"},"8":{"message":"Cell overflow"},"9":{"message":"Cell underflow"},"10":{"message":"Dictionary error"},"13":{"message":"Out of gas error"},"32":{"message":"Method ID not found"},"34":{"message":"Action is invalid or not supported"},"37":{"message":"Not enough TON"},"38":{"message":"Not enough extra-currencies"},"128":{"message":"Null reference exception"},"129":{"message":"Invalid serialization prefix"},"130":{"message":"Invalid incoming message"},"131":{"message":"Constraints error"},"132":{"message":"Access denied"},"133":{"message":"Contract stopped"},"134":{"message":"Invalid argument"}}}
Binary file modified sources/output/sample_SampleTactContract.boc
Binary file not shown.
Loading