1+ # import sys
2+ # sys.path.insert(0, '/Users/nam/desktop/injective/sdk-python/')
3+
14import asyncio
2- import aiohttp
35import logging
4- import json
5- import base64
6- import ecdsa
7- import sha3
8- import grpc
9-
10- from typing import Any , Dict , List
11- from injective .chain_client ._wallet import (
12- generate_wallet ,
13- privkey_to_address ,
14- privkey_to_pubkey ,
15- pubkey_to_address ,
16- seed_to_privkey ,
17- DEFAULT_BECH32_HRP ,
18- )
19- from injective .chain_client ._typings import SyncMode
20-
21-
22-
23- MIN_GAS_PRICE = 500000000
24-
25- class Transaction :
26-
27- def __init__ (
28- self ,
29- * ,
30- privkey : bytes ,
31- account_num : int ,
32- sequence : int ,
33- fee : int ,
34- gas : int ,
35- fee_denom : str = "inj" ,
36- memo : str = "" ,
37- chain_id : str = "injective-888" ,
38- hrp : str = DEFAULT_BECH32_HRP ,
39- sync_mode : SyncMode = "block" ,
40- ) -> None :
41- self ._privkey = privkey
42- self ._account_num = account_num
43- self ._sequence = sequence
44- self ._fee = fee
45- self ._fee_denom = fee_denom
46- self ._gas = gas
47- self ._memo = memo
48- self ._chain_id = chain_id
49- self ._hrp = hrp
50- self ._sync_mode = sync_mode
51- self ._msgs : List [dict ] = []
52-
53- def add_cosmos_bank_msg_send (self , recipient : str , amount : int , denom : str = "inj" ) -> None :
54- msg = {
55- "type" : "cosmos-sdk/MsgSend" ,
56- "value" : {
57- "from_address" : privkey_to_address (self ._privkey , hrp = self ._hrp ),
58- "to_address" : recipient ,
59- "amount" : [{"denom" : denom , "amount" : str (amount )}],
60- },
61- }
62- self ._msgs .append (msg )
63-
64- def get_signed (self ) -> str :
65- pubkey = privkey_to_pubkey (self ._privkey )
66- base64_pubkey = base64 .b64encode (pubkey ).decode ("utf-8" )
67- signed_tx = {
68- "tx" : {
69- "msg" : self ._msgs ,
70- "fee" : {
71- "gas" : str (self ._gas ),
72- "amount" : [{"denom" : self ._fee_denom , "amount" : str (self ._fee )}],
73- },
74- "memo" : self ._memo ,
75- "signatures" : [
76- {
77- "signature" : self ._sign (),
78- "pub_key" : {"type" : "injective/PubKeyEthSecp256k1" , "value" : base64_pubkey },
79- "account_number" : str (self ._account_num ),
80- "sequence" : str (self ._sequence ),
81- }
82- ],
83- },
84- "mode" : self ._sync_mode ,
85- }
86- return json .dumps (signed_tx , separators = ("," , ":" ))
876
88- def _sign (self ) -> str :
89- message_str = json .dumps (
90- self ._get_sign_message (), separators = ("," , ":" ), sort_keys = True )
91- message_bytes = message_str .encode ("utf-8" )
92-
93- privkey = ecdsa .SigningKey .from_string (
94- self ._privkey , curve = ecdsa .SECP256k1 )
95- signature_compact_keccak = privkey .sign_deterministic (
96- message_bytes , hashfunc = sha3 .keccak_256 , sigencode = ecdsa .util .sigencode_string_canonize
97- )
98- signature_base64_str = base64 .b64encode (
99- signature_compact_keccak ).decode ("utf-8" )
100- return signature_base64_str
101-
102- def _get_sign_message (self ) -> Dict [str , Any ]:
103- return {
104- "chain_id" : self ._chain_id ,
105- "account_number" : str (self ._account_num ),
106- "fee" : {
107- "gas" : str (self ._gas ),
108- "amount" : [{"amount" : str (self ._fee ), "denom" : self ._fee_denom }],
109- },
110- "memo" : self ._memo ,
111- "sequence" : str (self ._sequence ),
112- "msgs" : self ._msgs ,
113- }
7+ from pyinjective .composer import Composer as ProtoMsgComposer
8+ from pyinjective .client import Client
9+ from pyinjective .transaction import Transaction
10+ from pyinjective .constant import Network
11+ from pyinjective .wallet import PrivateKey , PublicKey , Address
11412
11513async def main () -> None :
116- sender_pk = seed_to_privkey (
117- "physical page glare junk return scale subject river token door mirror title"
118- )
119- sender_acc_addr = privkey_to_address (sender_pk )
120- print ("Sender Account:" , sender_acc_addr )
121-
122- acc_num , acc_seq = await get_account_num_seq (sender_acc_addr )
123-
124- tx = Transaction (
125- privkey = sender_pk ,
126- account_num = acc_num ,
127- sequence = acc_seq ,
128- gas = 200000 ,
129- fee = 200000 * MIN_GAS_PRICE ,
130- sync_mode = "block" ,
14+ # select network: localhost, testnet, mainnet
15+ network = Network .testnet ()
16+
17+ # initialize grpc client
18+ client = Client (network .grpc_endpoint , insecure = True )
19+
20+ # load account
21+ priv_key = PrivateKey .from_hex ("f9db9bf330e23cb7839039e944adef6e9df447b90b503d5b4464c90bea9022f3" )
22+ pub_key = priv_key .to_public_key ()
23+ address = pub_key .to_address ()
24+
25+ # prepare tx msg
26+ msg = ProtoMsgComposer .MsgSend (
27+ from_address = address .to_acc_bech32 (),
28+ to_address = 'inj14au322k9munkmx5wrchz9q30juf5wjgz2cfqku' ,
29+ amount = 1000000000000000000 ,
30+ denom = 'inj'
13131 )
132- tx .add_cosmos_bank_msg_send (
133- recipient = "inj1qy69k458ppmj45c3vqwcd6wvlcuvk23x0hsz58" ,
134- amount = 10000000000000000 ,
135- denom = "inj" ,
32+ acc_num , acc_seq = await address .get_num_seq (network .lcd_endpoint )
33+ gas_price = 500000000
34+ gas_limit = 200000
35+ fee = [ProtoMsgComposer .Coin (
36+ amount = str (gas_price * gas_limit ),
37+ denom = network .fee_denom ,
38+ )]
39+
40+ # build tx
41+ tx = (
42+ Transaction ()
43+ .with_messages (msg )
44+ .with_sequence (acc_seq )
45+ .with_account_num (acc_num )
46+ .with_chain_id (network .chain_id )
47+ .with_gas (gas_limit )
48+ .with_fee (fee )
49+ .with_memo ("" )
50+ .with_timeout_height (0 )
13651 )
13752
138- tx_json = tx .get_signed ()
139-
140- print ("Signed Tx:" , tx_json )
141- print ("Sent Tx:" , await post_tx (tx_json ))
142-
143- async def get_account_num_seq (address : str ) -> (int , int ):
144- async with aiohttp .ClientSession () as session :
145- async with session .request (
146- 'GET' , 'http://staking-lcd-testnet.injective.network/cosmos/auth/v1beta1/accounts/' + address ,
147- headers = {'Accept-Encoding' : 'application/json' },
148- ) as response :
149- if response .status != 200 :
150- print (await response .text ())
151- raise ValueError ("HTTP response status" , response .status )
152-
153- resp = json .loads (await response .text ())
154- acc = resp ['account' ]['base_account' ]
155- return acc ['account_number' ], acc ['sequence' ]
156-
157- async def post_tx (tx_json : str ):
158- async with aiohttp .ClientSession () as session :
159- async with session .request (
160- 'POST' , 'http://staking-lcd-testnet.injective.network/txs' , data = tx_json ,
161- headers = {'Content-Type' : 'application/json' },
162- ) as response :
163- if response .status != 200 :
164- print (await response .text ())
165- raise ValueError ("HTTP response status" , response .status )
53+ # build signed tx
54+ sign_doc = tx .get_sign_doc (pub_key )
55+ sig = priv_key .sign (sign_doc .SerializeToString ())
56+ tx_raw_bytes = tx .get_tx_data (sig , pub_key )
16657
167- resp = json .loads (await response .text ())
168- if 'code' in resp :
169- print ("Response:" , resp )
170- raise ValueError ('sdk error %d: %s' % (resp ['code' ], resp ['raw_log' ]))
58+ # broadcast tx: send_tx_async_mode, send_tx_sync_mode, send_tx_block_mode
59+ res = client .send_tx_block_mode (tx_raw_bytes )
17160
172- return resp ['txhash' ]
61+ # print tx response
62+ print (res )
17363
17464if __name__ == "__main__" :
17565 logging .basicConfig (level = logging .INFO )
176- asyncio .get_event_loop ().run_until_complete (main ())
66+ asyncio .get_event_loop ().run_until_complete (main ())
0 commit comments