forked from broxus/nekoton-python
-
Notifications
You must be signed in to change notification settings - Fork 1
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
Showing
6 changed files
with
376 additions
and
134 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 |
---|---|---|
@@ -1,3 +1,4 @@ | ||
from .nekoton import * | ||
|
||
from . import gql | ||
from . import contracts |
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,3 @@ | ||
from .base import IGiver | ||
from .giver import GiverV1 | ||
from .ever_wallet import EverWallet |
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 @@ | ||
import nekoton as _nt | ||
|
||
|
||
class IGiver: | ||
""" | ||
Abstract tokens giver. | ||
""" | ||
|
||
async def give(self, target: _nt.Address, amount: _nt.Tokens): | ||
raise NotImplementedError("IGiver is an abstract class") |
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,123 @@ | ||
from typing import Optional | ||
|
||
from ... import base as _base | ||
import nekoton as _nt | ||
|
||
_wallet_abi = _nt.ContractAbi("""{ | ||
"ABI version": 2, | ||
"version": "2.3", | ||
"header": ["pubkey", "time", "expire"], | ||
"functions": [{ | ||
"name": "sendTransaction", | ||
"inputs": [ | ||
{"name": "dest", "type": "address"}, | ||
{"name": "value", "type": "uint128"}, | ||
{"name": "bounce", "type": "bool"}, | ||
{"name": "flags", "type": "uint8"}, | ||
{"name": "payload", "type": "cell"} | ||
], | ||
"outputs": [] | ||
}], | ||
"events": [] | ||
}""") | ||
|
||
_send_transaction = _wallet_abi.get_function("sendTransaction") | ||
assert _send_transaction is not None | ||
|
||
_wallet_code = _nt.Cell.decode( | ||
"te6cckEBBgEA/AABFP8A9KQT9LzyyAsBAgEgAgMABNIwAubycdcBAcAA8nqDCNcY7UTQgwfXAdcLP8j4KM8WI88WyfkAA3HXAQHDAJqDB9cBURO68uBk3oBA1wGAINcBgCDXAVQWdfkQ8qj4I7vyeWa++COBBwiggQPoqFIgvLHydAIgghBM7mRsuuMPAcjL/8s/ye1UBAUAmDAC10zQ+kCDBtcBcdcBeNcB10z4AHCAEASqAhSxyMsFUAXPFlAD+gLLaSLQIc8xIddJoIQJuZgzcAHLAFjPFpcwcQHLABLM4skB+wAAPoIQFp4+EbqOEfgAApMg10qXeNcB1AL7AOjRkzLyPOI+zYS/" | ||
) | ||
_wallet_data_abi = [ | ||
("publicKey", _nt.AbiUint(256)), | ||
("timestamp", _nt.AbiUint(64)), | ||
] | ||
|
||
|
||
class EverWallet(_base.IGiver): | ||
@classmethod | ||
def compute_address( | ||
cls, public_key: _nt.PublicKey, workchain: int = 0 | ||
) -> _nt.Address: | ||
return cls.compute_state_init(public_key).compute_address(workchain) | ||
|
||
@staticmethod | ||
def compute_state_init(public_key: _nt.PublicKey) -> _nt.StateInit: | ||
data = _nt.Cell.build( | ||
abi=_wallet_data_abi, | ||
value={ | ||
"publicKey": public_key, | ||
"timestamp": 0, | ||
}, | ||
) | ||
return _nt.StateInit(_wallet_code, data) | ||
|
||
def __init__( | ||
self, transport: _nt.Transport, keypair: _nt.KeyPair, workchain: int = 0 | ||
): | ||
state_init = self.compute_state_init(keypair.public_key) | ||
|
||
self._initialized = False | ||
self._transport = transport | ||
self._keypair = keypair | ||
self._state_init = state_init | ||
self._address = state_init.compute_address(workchain) | ||
|
||
@property | ||
def address(self) -> _nt.Address: | ||
return self._address | ||
|
||
async def give(self, target: _nt.Address, amount: _nt.Tokens): | ||
await self.send(dst=target, value=amount, payload=_nt.Cell(), bounce=False) | ||
|
||
async def send( | ||
self, | ||
dst: _nt.Address, | ||
value: _nt.Tokens, | ||
payload: _nt.Cell, | ||
bounce: bool = False, | ||
) -> _nt.Transaction: | ||
state_init = await self.__get_state_init() | ||
|
||
signature_id = await self._transport.get_signature_id() | ||
|
||
external_message = _send_transaction.encode_external_message( | ||
self._address, | ||
input={ | ||
"dest": dst, | ||
"value": value, | ||
"bounce": bounce, | ||
"flags": 3, | ||
"payload": payload, | ||
}, | ||
public_key=self._keypair.public_key, | ||
state_init=state_init, | ||
).sign(self._keypair, signature_id) | ||
|
||
tx = await self._transport.send_external_message(external_message) | ||
if tx is None: | ||
raise RuntimeError("Message expired") | ||
return tx | ||
|
||
async def get_account_state(self) -> Optional[_nt.AccountState]: | ||
return await self._transport.get_account_state(self._address) | ||
|
||
async def get_balance(self) -> _nt.Tokens: | ||
state = await self.get_account_state() | ||
if state is None: | ||
return _nt.Tokens(0) | ||
else: | ||
return state.balance | ||
|
||
async def __get_state_init(self) -> Optional[_nt.StateInit]: | ||
if self._initialized: | ||
return None | ||
|
||
account_state = await self.get_account_state() | ||
if ( | ||
account_state is not None | ||
and account_state.status == _nt.AccountStatus.Active | ||
): | ||
self._initialized = True | ||
return None | ||
else: | ||
return self._state_init |
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,105 @@ | ||
from __future__ import annotations | ||
|
||
from ... import base as _base | ||
import nekoton as _nt | ||
|
||
_giver_v1_abi = _nt.ContractAbi("""{ | ||
"ABI version": 1, | ||
"functions": [{ | ||
"name": "constructor", | ||
"inputs": [], | ||
"outputs": [] | ||
}, { | ||
"name": "sendGrams", | ||
"inputs": [ | ||
{"name": "dest", "type": "address"}, | ||
{"name": "amount", "type": "uint64"} | ||
], | ||
"outputs": [] | ||
}], | ||
"events": [] | ||
}""") | ||
|
||
_giver_v1_constructor = _giver_v1_abi.get_function("constructor") | ||
_giver_v1_send_grams = _giver_v1_abi.get_function("sendGrams") | ||
_giver_v1_tvc = "te6ccgECJQEABaMAAgE0BgEBAcACAgPPIAUDAQHeBAAD0CAAQdgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAIo/wAgwAH0pCBYkvSg4YrtU1gw9KATBwEK9KQg9KEIAgPNQBAJAgHODQoCASAMCwAHDDbMIAAdPAZIbzyvCEhcHHwCl8CgAgEgDw4AASAA1T++wFkZWNvZGVfYWRkciD6QDL6QiBvECByuiFzurHy4H0hbxFu8uB9yHTPCwIibxLPCgcibxMicrqWI28TIs4ynyGBAQAi10mhz0AyICLOMuL+/AFkZWNvZGVfYWRkcjAhydAlVUFfBdswgAgEgEhEAK6T/fYCzsrovsTC2MLcxsvwTt4htmEAApaV/fYCwsa+6OTC3ObMyuWQ5Z6ARZ4UAOOegfBRnixJnixH9ATjnoDh9ATh9AUAgZ6B8EeeFj7lnoBBkkX2Af3+AsLGvujkwtzmzMrkvsrcyL4LAAgEgGhQB4P/+/QFtYWluX2V4dGVybmFsIY5Z/vwBZ2V0X3NyY19hZGRyINAg0wAycL2OGv79AWdldF9zcmNfYWRkcjBwyMnQVRFfAtsw4CBy1yExINMAMiH6QDP+/QFnZXRfc3JjX2FkZHIxISFVMV8E2zDYMSEVAfiOdf7+AWdldF9tc2dfcHVia2V5IMcCjhb+/wFnZXRfbXNnX3B1YmtleTFwMdsw4NUgxwGOF/7/AWdldF9tc2dfcHVia2V5MnAxMdsw4CCBAgDXIdcL/yL5ASIi+RDyqP7/AWdldF9tc2dfcHVia2V5MyADXwPbMNgixwKzFgHMlCLUMTPeJCIijjj++QFzdG9yZV9zaWdvACFvjCJvjCNvjO1HIW+M7UTQ9AVvjCDtV/79AXN0b3JlX3NpZ19lbmRfBdgixwGOE/78AW1zZ19pc19lbXB0eV8G2zDgItMfNCPTPzUgFwF2joDYji/+/gFtYWluX2V4dGVybmFsMiQiVXFfCPFAAf7+AW1haW5fZXh0ZXJuYWwzXwjbMOCAfPLwXwgYAf7++wFyZXBsYXlfcHJvdHBwcO1E0CD0BDI0IIEAgNdFmiDTPzIzINM/MjKWgggbd0Ay4iIluSX4I4ED6KgkoLmwjinIJAH0ACXPCz8izws/Ic8WIMntVP78AXJlcGxheV9wcm90Mn8GXwbbMOD+/AFyZXBsYXlfcHJvdDNwBV8FGQAE2zACASAcGwAPvOP3EDmG2YQCASAeHQCJuyXMvJ+ADwINM/MPAi/vwBcHVzaHBkYzd0b2M07UTQ9AHI7UdvEgH0ACHPFiDJ7VT+/QFwdXNocGRjN3RvYzQwXwLbMIAgEgIh8BCbiJACdQIAH+/v0BY29uc3RyX3Byb3RfMHBwgggbd0DtRNAg9AQyNCCBAIDXRY4UINI/MjMg0j8yMiBx10WUgHvy8N7eyCQB9AAjzws/Is8LP3HPQSHPFiDJ7VT+/QFjb25zdHJfcHJvdF8xXwX4ADDwIf78AXB1c2hwZGM3dG9jNO1E0PQByCEARO1HbxIB9AAhzxYgye1U/v0BcHVzaHBkYzd0b2M0MF8C2zAB4tz+/QFtYWluX2ludGVybmFsIY5Z/vwBZ2V0X3NyY19hZGRyINAg0wAycL2OGv79AWdldF9zcmNfYWRkcjBwyMnQVRFfAtsw4CBy1yExINMAMiH6QDP+/QFnZXRfc3JjX2FkZHIxISFVMV8E2zDYJCFwIwHqjjj++QFzdG9yZV9zaWdvACFvjCJvjCNvjO1HIW+M7UTQ9AVvjCDtV/79AXN0b3JlX3NpZ19lbmRfBdgixwCOHCFwuo4SIoIQXH7iB1VRXwbxQAFfBtsw4F8G2zDg/v4BbWFpbl9pbnRlcm5hbDEi0x80InG6JAA2niCAI1VhXwfxQAFfB9sw4CMhVWFfB/FAAV8H" | ||
|
||
|
||
class GiverV1(_base.IGiver): | ||
@staticmethod | ||
def compute_address(workchain: int = 0) -> _nt.Address: | ||
return _nt.Address.from_parts( | ||
workchain, _nt.Cell.decode(_giver_v1_tvc).repr_hash | ||
) | ||
|
||
@staticmethod | ||
async def deploy( | ||
transport: _nt.Transport, | ||
workchain: int = 0, | ||
other_giver: _base.IGiver | None = None, | ||
) -> GiverV1: | ||
# Compute giver address | ||
state_init_cell = _nt.Cell.decode(_giver_v1_tvc) | ||
address = _nt.Address.from_parts(workchain, state_init_cell.repr_hash) | ||
state_init = _nt.StateInit.from_cell(state_init_cell) | ||
|
||
# Ensure that giver account exists | ||
initial_balance = _nt.Tokens(1) | ||
state = await transport.get_account_state(address) | ||
if state is None: | ||
if other_giver is None: | ||
raise RuntimeError("Account does not have enough balance") | ||
|
||
tx = await other_giver.give(address, initial_balance) | ||
if tx is None: | ||
raise RuntimeError("Message expired") | ||
await transport.trace_transaction(tx).wait() | ||
|
||
# Deploy account | ||
if state.status == _nt.AccountStatus.Active: | ||
return GiverV1(transport, workchain) | ||
elif state.status == _nt.AccountStatus.Frozen: | ||
raise RuntimeError("Giver account is frozen") | ||
elif ( | ||
state.status == _nt.AccountStatus.Uninit and state.balance < initial_balance | ||
): | ||
tx = await other_giver.give(address, initial_balance) | ||
if tx is None: | ||
raise RuntimeError("Message expired") | ||
await transport.trace_transaction(tx).wait() | ||
|
||
external_message = _giver_v1_constructor.encode_external_message( | ||
address, | ||
input={}, | ||
public_key=None, | ||
state_init=state_init, | ||
).without_signature() | ||
tx = await transport.send_external_message(external_message) | ||
if tx is None: | ||
raise RuntimeError("Message expired") | ||
await transport.trace_transaction(tx).wait() | ||
|
||
return GiverV1(transport, workchain) | ||
|
||
def __init__(self, transport: _nt.Transport, workchain: int = 0): | ||
self._transport = transport | ||
self._address = GiverV1.compute_address(workchain) | ||
|
||
async def give(self, target: _nt.Address, amount: _nt.Tokens): | ||
# Prepare external message | ||
message = _giver_v1_send_grams.encode_external_message( | ||
self._address, | ||
input={ | ||
"dest": target, | ||
"amount": amount, | ||
}, | ||
public_key=None, | ||
).without_signature() | ||
|
||
# Send external message | ||
tx = await self._transport.send_external_message(message) | ||
if tx is None: | ||
raise RuntimeError("Message expired") | ||
|
||
# Wait until all transactions are produced | ||
await self._transport.trace_transaction(tx).wait() |
Oops, something went wrong.