2022-07-12 18:23:43 +00:00
import eth / keys
import eth / rlp
import eth / common
import eth / common / transaction as ct
2022-07-21 00:04:05 +00:00
import . / provider
2023-03-09 09:58:54 +00:00
import . / transaction as tx
2022-07-12 18:23:43 +00:00
import . / signer
export keys
2022-07-21 00:00:57 +00:00
var rng {. threadvar . } : ref HmacDrbgContext
proc getRng : ref HmacDrbgContext =
2022-07-14 08:22:54 +00:00
if rng . isNil :
2022-07-21 00:00:57 +00:00
rng = newRng ( )
2022-07-14 08:22:54 +00:00
rng
2022-07-12 18:23:43 +00:00
type SignableTransaction = common . Transaction
type WalletError * = object of EthersError
type Wallet * = ref object of Signer
privateKey * : PrivateKey
publicKey * : PublicKey
address * : Address
2022-07-21 00:04:05 +00:00
provider * : ? Provider
2022-07-12 18:23:43 +00:00
2022-07-21 00:04:05 +00:00
proc new * ( _ : type Wallet , pk : string , provider : Provider ) : Wallet =
2022-07-12 18:23:43 +00:00
result = Wallet ( )
result . privateKey = PrivateKey . fromHex ( pk ) . value
result . publicKey = result . privateKey . toPublicKey ( )
result . address = Address . init ( result . publicKey . toCanonicalAddress ( ) )
result . provider = some provider
proc new * ( _ : type Wallet , pk : string ) : Wallet =
result = Wallet ( )
result . privateKey = PrivateKey . fromHex ( pk ) . value
result . publicKey = result . privateKey . toPublicKey ( )
result . address = Address . init ( result . publicKey . toCanonicalAddress ( ) )
2022-07-21 00:04:05 +00:00
proc connect * ( wallet : Wallet , provider : Provider ) =
2022-07-12 18:23:43 +00:00
wallet . provider = some provider
proc createRandom * ( _ : type Wallet ) : Wallet =
result = Wallet ( )
2022-07-21 00:00:57 +00:00
result . privateKey = PrivateKey . random ( getRng ( ) [ ] )
2022-07-12 18:23:43 +00:00
result . publicKey = result . privateKey . toPublicKey ( )
result . address = Address . init ( result . publicKey . toCanonicalAddress ( ) )
2022-07-21 00:04:05 +00:00
proc createRandom * ( _ : type Wallet , provider : Provider ) : Wallet =
2022-07-12 18:23:43 +00:00
result = Wallet ( )
2022-07-21 00:00:57 +00:00
result . privateKey = PrivateKey . random ( getRng ( ) [ ] )
2022-07-12 18:23:43 +00:00
result . publicKey = result . privateKey . toPublicKey ( )
result . address = Address . init ( result . publicKey . toCanonicalAddress ( ) )
result . provider = some provider
2022-07-12 19:13:34 +00:00
method provider * ( wallet : Wallet ) : Provider =
2022-07-14 09:24:21 +00:00
without provider = ? wallet . provider :
2022-07-12 19:13:34 +00:00
raise newException ( WalletError , " Wallet has no provider " )
2022-07-14 09:24:21 +00:00
provider
2022-07-12 19:13:34 +00:00
2022-07-21 00:00:57 +00:00
method getAddress ( wallet : Wallet ) : Future [ Address ] {. async . } =
return wallet . address
2022-07-12 18:23:43 +00:00
proc signTransaction ( tr : var SignableTransaction , pk : PrivateKey ) =
let h = tr . txHashNoSignature
let s = sign ( pk , SkMessage ( h . data ) )
let r = toRaw ( s )
let v = r [ 64 ]
2023-03-09 09:58:54 +00:00
tr . R = fromBytesBE ( UInt256 , r . toOpenArray ( 0 , 31 ) )
2022-07-12 18:23:43 +00:00
tr . S = fromBytesBE ( UInt256 , r . toOpenArray ( 32 , 63 ) )
case tr . txType :
of TxLegacy :
#tr.V = int64(v) + int64(tr.chainId)*2 + 35 #TODO does not work, not sure why. Sending the tx results in error of too little funds. Maybe something wrong with signature and a wrong sender gets encoded?
tr . V = int64 ( v ) + 27
of TxEip1559 :
tr . V = int64 ( v )
else :
raise newException ( WalletError , " Transaction type not supported " )
2023-03-09 09:58:54 +00:00
proc signTransaction * ( wallet : Wallet , tx : tx . Transaction ) : Future [ seq [ byte ] ] {. async . } =
2022-07-14 09:24:21 +00:00
if sender = ? tx . sender and sender ! = wallet . address :
raise newException ( WalletError , " from address mismatch " )
without nonce = ? tx . nonce and chainId = ? tx . chainId and gasLimit = ? tx . gasLimit :
raise newException ( WalletError , " Transaction is properly populated " )
2022-07-12 18:23:43 +00:00
var s : SignableTransaction
2022-07-14 09:24:21 +00:00
if maxFee = ? tx . maxFee and maxPriorityFee = ? tx . maxPriorityFee :
2022-07-12 18:23:43 +00:00
s . txType = TxEip1559
2022-07-14 09:24:21 +00:00
s . maxFee = GasInt ( maxFee . truncate ( uint64 ) )
s . maxPriorityFee = GasInt ( maxPriorityFee . truncate ( uint64 ) )
elif gasPrice = ? tx . gasPrice :
2022-07-12 18:23:43 +00:00
s . txType = TxLegacy
2022-07-14 09:24:21 +00:00
s . gasPrice = GasInt ( gasPrice . truncate ( uint64 ) )
else :
raise newException ( WalletError , " Transaction is properly populated " )
s . chainId = ChainId ( chainId . truncate ( uint64 ) )
s . gasLimit = GasInt ( gasLimit . truncate ( uint64 ) )
s . nonce = nonce . truncate ( uint64 )
2022-07-12 18:23:43 +00:00
s . to = some EthAddress ( tx . to )
s . payload = tx . data
signTransaction ( s , wallet . privateKey )
2023-03-09 09:58:54 +00:00
2022-07-21 00:00:57 +00:00
return rlp . encode ( s )
2022-07-12 19:21:35 +00:00
2023-03-09 09:58:54 +00:00
method sendTransaction * ( wallet : Wallet , tx : tx . Transaction ) : Future [ TransactionResponse ] {. async . } =
2022-07-21 00:00:57 +00:00
let rawTX = await signTransaction ( wallet , tx )
2022-07-14 09:51:24 +00:00
return await provider ( wallet ) . sendTransaction ( rawTX )
2022-07-12 19:21:35 +00:00
#TODO add functionality to sign messages
2022-07-14 08:22:54 +00:00
#TODO add functionality to create wallets from Mnemoniks or Keystores