-
Notifications
You must be signed in to change notification settings - Fork 28
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Update Makefile to collect and generate all assets following pyband layout * Include temp action on this branch * Init implementation for grpc chain client * WIP refactoring the pkg * WIP refactoring the pkg * WIP refactoring the pkg * WIP refactoring the pkg * WIP refactoring the pkg * Construct base components for pkg * Add subaccount, accunt num_seq getter, adapt signing hash fuction * Add timeout height option to tx * Add network constants * Update example with new pkg layout * Add import lines to proto pkg * add exchange grpc endpoint to client, update exchange_api usage * Refactor backend price, quantity calculation * Refactor derivative limit order example * refactor examples * Add denoms.ini and refactor constant.py * Update readme and remove temp action hook * wip: example client * Add back all utils conversion methods * Fetch all markets metadata and save to denoms.ini * Revert client.py changes, add composer class to build proto msgs * Update chain client examples * Update example import usage * Remove trigger price from composer methods * Prepare to gen proto files * generate complete proto files Co-authored-by: Achilleas <[email protected]> Co-authored-by: Albert Chon <[email protected]>
- Loading branch information
1 parent
181d57a
commit 124f8d1
Showing
428 changed files
with
72,977 additions
and
15,278 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
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,2 @@ | ||
recursive-include pyinjective/proto *.py | ||
include pyinjective/denoms.ini |
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,19 +1,28 @@ | ||
all: | ||
|
||
EXCHANGE_PROTO_FILES=$(shell find ../injective-exchange/api/gen/grpc -type f -name '*.proto') | ||
PROTO_DIRS=$(shell find ./proto -path -prune -o -name '*.proto' -print0 | xargs -0 -n1 dirname | sort | uniq) | ||
gen: gen-client | ||
|
||
gen-client: copy-proto | ||
python -m grpc_tools.protoc -I./exchange_api/pb/ \ | ||
--python_out=./exchange_api/ \ | ||
--grpc_python_out=./exchange_api/ \ | ||
$(shell find ./exchange_api/pb -type f -name '*.proto') | ||
|
||
SRC_PROTO_FILES = $(shell find ../injective-exchange/api/gen/grpc -type f -name '*.proto') | ||
@for dir in $(PROTO_DIRS); do \ | ||
mkdir -p ./pyinjective/$${dir}; \ | ||
python3 -m grpc_tools.protoc \ | ||
-I proto \ | ||
--python_out=./pyinjective/proto \ | ||
--grpc_python_out=./pyinjective/proto \ | ||
$$(find $${dir} -type file -name '*.proto'); \ | ||
done; \ | ||
rm -rf proto | ||
echo "import os\nimport sys\n\nsys.path.insert(0, os.path.abspath(os.path.dirname(__file__)))" > pyinjective/proto/__init__.py | ||
|
||
copy-proto: | ||
mkdir -p exchange_api/pb/ | ||
@for file in $(SRC_PROTO_FILES) ; do \ | ||
cp "$${file}" exchange_api/pb/ ;\ | ||
done | ||
rm -rf pyinjective/proto | ||
mkdir -p proto/exchange | ||
cp -r ../injective-core/proto/injective proto/ | ||
cp -r ../injective-core/third_party/proto/ proto/ | ||
@for file in $(EXCHANGE_PROTO_FILES); do \ | ||
cp "$${file}" proto/exchange/; \ | ||
done | ||
|
||
.PHONY: all gen gen-client copy-proto |
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
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,176 +1,66 @@ | ||
# import sys | ||
# sys.path.insert(0, '/Users/nam/desktop/injective/sdk-python/') | ||
|
||
import asyncio | ||
import aiohttp | ||
import logging | ||
import json | ||
import base64 | ||
import ecdsa | ||
import sha3 | ||
import grpc | ||
|
||
from typing import Any, Dict, List | ||
from injective.chain_client._wallet import ( | ||
generate_wallet, | ||
privkey_to_address, | ||
privkey_to_pubkey, | ||
pubkey_to_address, | ||
seed_to_privkey, | ||
DEFAULT_BECH32_HRP, | ||
) | ||
from injective.chain_client._typings import SyncMode | ||
|
||
|
||
|
||
MIN_GAS_PRICE = 500000000 | ||
|
||
class Transaction: | ||
|
||
def __init__( | ||
self, | ||
*, | ||
privkey: bytes, | ||
account_num: int, | ||
sequence: int, | ||
fee: int, | ||
gas: int, | ||
fee_denom: str = "inj", | ||
memo: str = "", | ||
chain_id: str = "injective-888", | ||
hrp: str = DEFAULT_BECH32_HRP, | ||
sync_mode: SyncMode = "block", | ||
) -> None: | ||
self._privkey = privkey | ||
self._account_num = account_num | ||
self._sequence = sequence | ||
self._fee = fee | ||
self._fee_denom = fee_denom | ||
self._gas = gas | ||
self._memo = memo | ||
self._chain_id = chain_id | ||
self._hrp = hrp | ||
self._sync_mode = sync_mode | ||
self._msgs: List[dict] = [] | ||
|
||
def add_cosmos_bank_msg_send(self, recipient: str, amount: int, denom: str = "inj") -> None: | ||
msg = { | ||
"type": "cosmos-sdk/MsgSend", | ||
"value": { | ||
"from_address": privkey_to_address(self._privkey, hrp=self._hrp), | ||
"to_address": recipient, | ||
"amount": [{"denom": denom, "amount": str(amount)}], | ||
}, | ||
} | ||
self._msgs.append(msg) | ||
|
||
def get_signed(self) -> str: | ||
pubkey = privkey_to_pubkey(self._privkey) | ||
base64_pubkey = base64.b64encode(pubkey).decode("utf-8") | ||
signed_tx = { | ||
"tx": { | ||
"msg": self._msgs, | ||
"fee": { | ||
"gas": str(self._gas), | ||
"amount": [{"denom": self._fee_denom, "amount": str(self._fee)}], | ||
}, | ||
"memo": self._memo, | ||
"signatures": [ | ||
{ | ||
"signature": self._sign(), | ||
"pub_key": {"type": "injective/PubKeyEthSecp256k1", "value": base64_pubkey}, | ||
"account_number": str(self._account_num), | ||
"sequence": str(self._sequence), | ||
} | ||
], | ||
}, | ||
"mode": self._sync_mode, | ||
} | ||
return json.dumps(signed_tx, separators=(",", ":")) | ||
|
||
def _sign(self) -> str: | ||
message_str = json.dumps( | ||
self._get_sign_message(), separators=(",", ":"), sort_keys=True) | ||
message_bytes = message_str.encode("utf-8") | ||
|
||
privkey = ecdsa.SigningKey.from_string( | ||
self._privkey, curve=ecdsa.SECP256k1) | ||
signature_compact_keccak = privkey.sign_deterministic( | ||
message_bytes, hashfunc=sha3.keccak_256, sigencode=ecdsa.util.sigencode_string_canonize | ||
) | ||
signature_base64_str = base64.b64encode( | ||
signature_compact_keccak).decode("utf-8") | ||
return signature_base64_str | ||
|
||
def _get_sign_message(self) -> Dict[str, Any]: | ||
return { | ||
"chain_id": self._chain_id, | ||
"account_number": str(self._account_num), | ||
"fee": { | ||
"gas": str(self._gas), | ||
"amount": [{"amount": str(self._fee), "denom": self._fee_denom}], | ||
}, | ||
"memo": self._memo, | ||
"sequence": str(self._sequence), | ||
"msgs": self._msgs, | ||
} | ||
from pyinjective.composer import Composer as ProtoMsgComposer | ||
from pyinjective.client import Client | ||
from pyinjective.transaction import Transaction | ||
from pyinjective.constant import Network | ||
from pyinjective.wallet import PrivateKey, PublicKey, Address | ||
|
||
async def main() -> None: | ||
sender_pk = seed_to_privkey( | ||
"physical page glare junk return scale subject river token door mirror title" | ||
) | ||
sender_acc_addr = privkey_to_address(sender_pk) | ||
print("Sender Account:", sender_acc_addr) | ||
|
||
acc_num, acc_seq = await get_account_num_seq(sender_acc_addr) | ||
|
||
tx = Transaction( | ||
privkey=sender_pk, | ||
account_num=acc_num, | ||
sequence=acc_seq, | ||
gas=200000, | ||
fee=200000 * MIN_GAS_PRICE, | ||
sync_mode="block", | ||
# select network: localhost, testnet, mainnet | ||
network = Network.testnet() | ||
|
||
# initialize grpc client | ||
client = Client(network.grpc_endpoint, insecure=True) | ||
|
||
# load account | ||
priv_key = PrivateKey.from_hex("f9db9bf330e23cb7839039e944adef6e9df447b90b503d5b4464c90bea9022f3") | ||
pub_key = priv_key.to_public_key() | ||
address = pub_key.to_address() | ||
|
||
# prepare tx msg | ||
msg = ProtoMsgComposer.MsgSend( | ||
from_address=address.to_acc_bech32(), | ||
to_address='inj14au322k9munkmx5wrchz9q30juf5wjgz2cfqku', | ||
amount=1000000000000000000, | ||
denom='inj' | ||
) | ||
tx.add_cosmos_bank_msg_send( | ||
recipient="inj1qy69k458ppmj45c3vqwcd6wvlcuvk23x0hsz58", | ||
amount=10000000000000000, | ||
denom="inj", | ||
acc_num, acc_seq = await address.get_num_seq(network.lcd_endpoint) | ||
gas_price = 500000000 | ||
gas_limit = 200000 | ||
fee = [ProtoMsgComposer.Coin( | ||
amount=str(gas_price * gas_limit), | ||
denom=network.fee_denom, | ||
)] | ||
|
||
# build tx | ||
tx = ( | ||
Transaction() | ||
.with_messages(msg) | ||
.with_sequence(acc_seq) | ||
.with_account_num(acc_num) | ||
.with_chain_id(network.chain_id) | ||
.with_gas(gas_limit) | ||
.with_fee(fee) | ||
.with_memo("") | ||
.with_timeout_height(0) | ||
) | ||
|
||
tx_json = tx.get_signed() | ||
|
||
print("Signed Tx:", tx_json) | ||
print("Sent Tx:", await post_tx(tx_json)) | ||
|
||
async def get_account_num_seq(address: str) -> (int, int): | ||
async with aiohttp.ClientSession() as session: | ||
async with session.request( | ||
'GET', 'http://staking-lcd-testnet.injective.network/cosmos/auth/v1beta1/accounts/' + address, | ||
headers={'Accept-Encoding': 'application/json'}, | ||
) as response: | ||
if response.status != 200: | ||
print(await response.text()) | ||
raise ValueError("HTTP response status", response.status) | ||
|
||
resp = json.loads(await response.text()) | ||
acc = resp['account']['base_account'] | ||
return acc['account_number'], acc['sequence'] | ||
|
||
async def post_tx(tx_json: str): | ||
async with aiohttp.ClientSession() as session: | ||
async with session.request( | ||
'POST', 'http://staking-lcd-testnet.injective.network/txs', data=tx_json, | ||
headers={'Content-Type': 'application/json'}, | ||
) as response: | ||
if response.status != 200: | ||
print(await response.text()) | ||
raise ValueError("HTTP response status", response.status) | ||
# build signed tx | ||
sign_doc = tx.get_sign_doc(pub_key) | ||
sig = priv_key.sign(sign_doc.SerializeToString()) | ||
tx_raw_bytes = tx.get_tx_data(sig, pub_key) | ||
|
||
resp = json.loads(await response.text()) | ||
if 'code' in resp: | ||
print("Response:", resp) | ||
raise ValueError('sdk error %d: %s' % (resp['code'], resp['raw_log'])) | ||
# broadcast tx: send_tx_async_mode, send_tx_sync_mode, send_tx_block_mode | ||
res = client.send_tx_block_mode(tx_raw_bytes) | ||
|
||
return resp['txhash'] | ||
# print tx response | ||
print(res) | ||
|
||
if __name__ == "__main__": | ||
logging.basicConfig(level=logging.INFO) | ||
asyncio.get_event_loop().run_until_complete(main()) | ||
asyncio.get_event_loop().run_until_complete(main()) |
Oops, something went wrong.