Skip to content

Commit 2ce19f2

Browse files
merge: implementation of higher levels
2 parents 72e0dfb + 86b4b2a commit 2ce19f2

90 files changed

Lines changed: 4258 additions & 412 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Created by .ignore support plugin (hsz.mobi)
22
# Project
33
test_space/
4+
.build/
45

56
# VSCode
67
.vscode/

README.md

Lines changed: 48 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<img align="left" width="64" height="64" src="./assets/dbuilder-icon.png">
22

33
# dBuilder.py
4-
[![PyPI version](https://img.shields.io/badge/dbuilder-0.2.0-informational?style=flat-square&color=FFFF91)](https://pypi.org/project/dbuilder/)
4+
[![PyPI version](https://img.shields.io/badge/dbuilder-0.7.0-informational?style=flat-square&color=FFFF91)](https://pypi.org/project/dbuilder/)
55
[![Telegram](https://img.shields.io/badge/Telegram-@d__builder-informational?style=flat-square&color=0088cc)](https://t.me/d_builder)
66

77
dBuilder.py is smart contract development framework in Python for [TON (The Open Network)](https://ton.org). Its purpose is to make the development, testing, and deployment procedures much easier!
@@ -12,67 +12,52 @@ dBuilder.py is smart contract development framework in Python for [TON (The Open
1212
- Utilize Python's syntax to provide code reuse, understandable, and organized code that is simple to test
1313

1414
## Overview
15-
The major goal of dBuilder is to make contract development easier for TON by avoiding the steep learning curve of `FunC`. The present version implements a one-to-one mapping of Python to FunC with no special simplifications. However, the higher layers above the base are still under construction. Currently, [Simple wallet contract](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/wallet-code.fc) looks like this in dBuilder:
15+
dBuilder's main purpose is to make contract building simpler for TON by bypassing the steep learning curve of `FunC`. dBuilder, by exploiting Python's OOP features, will enable you to create with more ease and less worry. In dBuilder, here is how the [Simple wallet contract](https://github.com/ton-blockchain/ton/blob/master/crypto/smartcont/wallet-code.fc) looks:
1616

1717
```python
18-
from dbuilder import method, method_id
19-
from dbuilder.core.loop import while_
20-
from dbuilder.func.contract import Contract
21-
from dbuilder.types import Slice
18+
from dbuilder import *
2219

2320

2421
class SimpleWallet(Contract):
22+
"""
23+
Simple Wallet Contract.
24+
25+
# config
26+
get-methods:
27+
- seq_no
28+
- public_key
29+
"""
30+
31+
class Data(Model):
32+
seq_no: uint32
33+
public_key: uint256
34+
35+
class ExternalBody(Payload):
36+
signature: slice[512]
37+
seq_no: uint32
38+
valid_until: uint32
39+
40+
data: Data
41+
2542
def external_receive(
26-
self,
43+
ctx,
2744
in_msg: Slice,
2845
) -> None:
29-
super(SimpleWallet, self).external_receive(
30-
in_msg,
31-
)
32-
signature = in_msg.load_bits_(512)
33-
cs = in_msg
34-
msg_seqno = cs.load_uint_(32)
35-
valid_until = cs.load_uint_(32)
36-
self.throw_if(35, valid_until <= self.now())
37-
ds = self.get_data().begin_parse()
38-
stored_seqno = ds.load_uint_(32)
39-
public_key = ds.load_uint_(256)
40-
ds.end_parse()
41-
self.throw_unless(33, msg_seqno == stored_seqno)
42-
self.throw_unless(
43-
34,
44-
self.check_signature(
45-
self.slice_hash(in_msg),
46-
signature,
47-
public_key,
48-
),
49-
)
50-
self.accept_message()
51-
cs.touch_()
52-
with while_(cs.slice_refs()):
53-
mode = cs.load_uint_(8)
54-
self.send_raw_message(cs.load_ref_(), mode)
55-
cs.end_parse()
56-
self.set_data(
57-
self.begin_cell()
58-
.store_uint(stored_seqno + 1, 32)
59-
.store_uint(public_key, 256)
60-
.end_cell(),
61-
)
62-
63-
@method_id
64-
@method
65-
def seqno(self) -> int:
66-
return self.get_data().begin_parse().preload_uint(32)
67-
68-
@method_id
69-
@method
70-
def get_public_key(self) -> int:
71-
cs = self.get_data().begin_parse()
72-
cs.load_uint_(32)
73-
return cs.preload_uint(256)
46+
msg = ctx.ExternalBody(in_msg)
47+
assert msg.valid_until > std.now(), 35
48+
assert msg.seq_no == ctx.data.seq_no, 33
49+
assert std.check_signature(
50+
msg.hash(after="signature"),
51+
msg.signature,
52+
ctx.data.public_key,
53+
), 34
54+
std.accept_message()
55+
while msg.refs():
56+
mode = msg >> uint8
57+
std.send_raw_message(msg >> Ref[Cell], mode)
58+
ctx.data.seq_no += 1
59+
ctx.data.save()
7460
```
75-
Full documentation with specifics is being worked on and will be published soon!
7661

7762
## Quick Start
7863

@@ -96,51 +81,24 @@ dbuilder init <project-name>
9681
dbuilder build
9782
```
9883

99-
## Vision
100-
As previously stated, the key advantage of dBuilder is the simplifications that will be available as higher layers are developed. If you're curious about how Simple wallet contract implementation may appear in the future, here's a look:
101-
102-
```python
103-
class SimpleWallet(Contract):
104-
class Data:
105-
seq_no: UInt(32)
106-
public_key: UInt(256)
107-
getters = [seq_no, public_key]
108-
109-
class Message:
110-
signature: UInt(512)
111-
seq_no: UInt(32)
112-
valid_until: UInt(32)
113-
114-
def external_receive(
115-
self,
116-
in_msg: Slice,
117-
) -> None:
118-
msg = Message(in_msg)
119-
assert msg.valid_until > self.now(), 35
120-
assert msg.seq_no == self.data.seq_no, 33
121-
assert self.check_signature(
122-
msg.hash(after="signature"),
123-
msg.signature,
124-
self.data.public_key,
125-
), 34
126-
self.accept_message()
84+
## Standard Contracts Implementation
85+
- [x] Jetton Implementation ([jetton-impl](https://github.com/decentralized-builder/jetton-impl))
86+
- [ ] NFT Implementation
87+
- [ ] DEX Implementation
12788

128-
with msg.has_ref():
129-
mode = msg.uint(8)
130-
self.send_raw_message(msg.ref(), mode)
13189

132-
self.data.seq_no += 1
133-
self.data.save()
134-
```
90+
## Documentation and Examples
91+
Full documentation with specifications is being developed and will be available shortly!
92+
Until then, you may look at standard contracts implementation; they cover the majority of the ideas required; if you're looking for more, take a glance at the 'test/' directory for some demonstrations of dBuilder's capabilities.
13593

13694
## Roadmap
13795

13896
### Milestone 1: Python Framework for contract development
13997

14098
- [x] Semi One-to-One mapping of functions and expressions (Base Compiler, Python -> FunC)
141-
- [ ] First higher layer over the base mappings to simplify type calls (leveraging OOP capabilities)
142-
- [ ] Second higher layer over the base, simplifying contract developments towards maximizing code reusability and simplicity (leveraging Meta programming capabilities)
143-
- [ ] Providing standard smart contracts implementation with dBuilder
99+
- [x] First higher layer over the base mappings to simplify type calls (leveraging OOP capabilities)
100+
- [x] Second higher layer over the base, simplifying contract developments towards maximizing code reusability and simplicity (leveraging Meta programming capabilities)
101+
- [x] Providing standard smart contracts implementation with dBuilder
144102

145103
### Milestone 2: deploying, testing, interaction capabilities
146104
- [ ] Simple interaction interface with TON Blockchain

dbuilder/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from dbuilder.ast import *
22
from dbuilder.core import *
33
from dbuilder.func import *
4+
from dbuilder.library import *
45
from dbuilder.types import *

dbuilder/ast/ast_patcher.py

Lines changed: 21 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -7,152 +7,29 @@
77
"""
88

99
import ast
10-
from typing import Any
1110

12-
13-
class Transformer(ast.NodeTransformer):
14-
"""Transforms the AST to capture assginments."""
15-
16-
def generic_visit(self, node):
17-
ast.NodeTransformer.generic_visit(self, node)
18-
return node
19-
20-
def visit_Return(self, node: ast.Return) -> Any:
21-
v = node.value
22-
if v is None:
23-
v = ast.Constant(None)
24-
u_node = ast.Expr(
25-
value=ast.Call(
26-
func=ast.Attribute(
27-
value=ast.Name(id="self", ctx=ast.Load()),
28-
attr="ret_",
29-
ctx=ast.Load(),
30-
),
31-
args=[v],
32-
keywords=[],
33-
),
34-
)
35-
ast.fix_missing_locations(u_node)
36-
return u_node
37-
pass
38-
39-
def visit_Assign(self, node):
40-
tg = node.targets[0]
41-
if isinstance(tg, ast.Tuple):
42-
node.targets = [
43-
ast.Name(
44-
id="__tmp__",
45-
ctx=ast.Store(),
46-
),
47-
]
48-
vars = [v.id for v in tg.dims]
49-
e_expr = ast.Assign(
50-
targets=[
51-
ast.Tuple(
52-
elts=[
53-
ast.Name(
54-
id=v,
55-
ctx=ast.Store(),
56-
)
57-
for v in vars
58-
],
59-
ctx=ast.Store(),
60-
),
61-
],
62-
value=ast.Name(
63-
id="__tmp__",
64-
ctx=ast.Load(),
65-
),
66-
)
67-
l_expr = ast.Call(
68-
func=ast.Name(id="hasattr", ctx=ast.Load()),
69-
args=[
70-
ast.Name(id="__tmp__", ctx=ast.Load()),
71-
ast.Constant(value="__prep_unpack__", kind=None),
72-
],
73-
keywords=[],
74-
)
75-
r_expr = ast.Call(
76-
func=ast.Attribute(
77-
value=ast.Name(id="__tmp__", ctx=ast.Load()),
78-
attr="__prep_unpack__",
79-
ctx=ast.Load(),
80-
),
81-
args=[ast.Constant(value=len(vars), kind=None)],
82-
keywords=[],
83-
)
84-
85-
p_expr = ast.Expr(
86-
value=ast.BoolOp(op=ast.And(), values=[l_expr, r_expr]),
87-
)
88-
a_expr = ast.Expr(
89-
value=ast.Call(
90-
func=ast.Attribute(
91-
value=ast.Name(id="__tmp__", ctx=ast.Load()),
92-
attr="__massign__",
93-
ctx=ast.Load(),
94-
),
95-
args=[
96-
ast.List(
97-
elts=[
98-
ast.Constant(value=str(v), kind=None)
99-
for v in vars
100-
],
101-
ctx=ast.Load(),
102-
),
103-
ast.List(
104-
elts=[
105-
ast.Name(id=str(v), ctx=ast.Load())
106-
for v in vars
107-
],
108-
ctx=ast.Load(),
109-
),
110-
],
111-
keywords=[],
112-
),
113-
)
114-
nodes = [node, p_expr, e_expr, a_expr]
115-
else:
116-
target = node.targets[0].id
117-
nodes = [node]
118-
if isinstance(node.value, ast.Constant):
119-
if isinstance(node.value.value, int):
120-
node.value = ast.Call(
121-
func=ast.Attribute(
122-
value=ast.Name(id="self", ctx=ast.Load()),
123-
attr="factory_",
124-
ctx=ast.Load(),
125-
),
126-
args=[ast.Constant("int"), node.value],
127-
keywords=[],
128-
)
129-
130-
c_expr = ast.Call(
131-
func=ast.Attribute(
132-
value=ast.Name(id=target, ctx=ast.Load()),
133-
attr="__assign__",
134-
ctx=ast.Load(),
135-
),
136-
args=[ast.Constant(value=str(target), kind=None)],
137-
keywords=[],
138-
)
139-
if isinstance(node.value, ast.Name):
140-
a_expr = ast.Assign(
141-
targets=[ast.Name(id=target, ctx=ast.Store())],
142-
value=c_expr,
143-
)
144-
else:
145-
a_expr = ast.Expr(
146-
value=c_expr,
147-
)
148-
nodes.append(a_expr)
149-
for g_node in nodes:
150-
ast.fix_missing_locations(g_node)
151-
return nodes
11+
from dbuilder.ast.patchers import (
12+
AssertPatcher,
13+
AssignPatcher,
14+
IfPatcher,
15+
RaisePatcher,
16+
ReturnPatcher,
17+
UnaryPatcher,
18+
WhilePatcher,
19+
)
15220

15321

15422
def patch(node):
155-
trans = Transformer()
156-
new_node = trans.visit(node)
157-
ast.fix_missing_locations(new_node)
23+
transformers = [
24+
UnaryPatcher(),
25+
AssertPatcher(),
26+
AssignPatcher(),
27+
IfPatcher(),
28+
RaisePatcher(),
29+
ReturnPatcher(),
30+
WhilePatcher(),
31+
]
32+
for t in transformers:
33+
new_node = t.visit(node)
34+
ast.fix_missing_locations(new_node)
15835
return new_node

dbuilder/ast/bool_dict.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from collections.abc import MutableMapping
2+
3+
4+
class BoolDict(MutableMapping):
5+
def __init__(self, default_=False, *args, **kwargs):
6+
self.store = {}
7+
self.default = default_
8+
self.update(dict(*args, **kwargs)) # use the free update to set keys
9+
10+
def has(self, item):
11+
return item in self.store
12+
13+
def __getitem__(self, key):
14+
if key not in self.store:
15+
return self.default
16+
return self.store[self._keytransform(key)]
17+
18+
def __setitem__(self, key, value):
19+
self.store[self._keytransform(key)] = value
20+
21+
def __delitem__(self, key):
22+
del self.store[self._keytransform(key)]
23+
24+
def __iter__(self):
25+
return iter(self.store)
26+
27+
def __len__(self):
28+
return len(self.store)
29+
30+
def _keytransform(self, key):
31+
return key

0 commit comments

Comments
 (0)