├── .gitignore ├── .vscode └── launch.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── contracts ├── fungible-token.scilla ├── hello_world.scilla ├── helloworld.txt └── helloworldversion.txt ├── example └── example.dart ├── lib ├── laksadart.dart └── src │ ├── account │ ├── account.dart │ ├── address.dart │ ├── api.dart │ ├── index.dart │ └── wallet.dart │ ├── contract │ ├── abi.dart │ ├── api.dart │ ├── contract.dart │ ├── factory.dart │ ├── index.dart │ ├── testScilla.dart │ └── util.dart │ ├── core │ ├── index.dart │ └── zilliqa_module.dart │ ├── crypto │ ├── bech32.dart │ ├── checksum.dart │ ├── dartRandom.dart │ ├── hmac-drbg.dart │ ├── index.dart │ ├── isolates.dart │ ├── keystore │ │ ├── api.dart │ │ ├── function.dart │ │ ├── key_derivator.dart │ │ ├── key_store.dart │ │ └── util.dart │ ├── schnorr.dart │ ├── sha256digest.dart │ └── signature.dart │ ├── data │ └── network │ │ ├── network_info.dart │ │ ├── network_info.freezed.dart │ │ └── network_info.g.dart │ ├── messenger │ ├── Blockchain.dart │ ├── Messenger.dart │ └── index.dart │ ├── proto │ ├── message.pb.dart │ ├── message.pbenum.dart │ ├── message.pbjson.dart │ ├── message.pbserver.dart │ └── message.proto │ ├── provider │ ├── Base.dart │ ├── Http.dart │ ├── Middleware.dart │ ├── index.dart │ └── net.dart │ ├── transaction │ ├── api.dart │ ├── factory.dart │ ├── index.dart │ ├── transaction.dart │ └── util.dart │ ├── utils │ ├── common.dart │ ├── index.dart │ ├── network.dart │ ├── numbers.dart │ ├── transaction.dart │ ├── unit.dart │ └── validators.dart │ └── zilliqa.dart ├── pubspec.yaml ├── scripts ├── commit.sh └── pub.sh └── test ├── contract.dart ├── fixtures ├── keystores.json └── schnorr.signature.json ├── key_gen.dart ├── keystore.dart ├── proto.dart └── schnorr.dart /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .dart_tool 3 | _experimental.dart 4 | pedantic*.yaml 5 | .packages 6 | pubspec.lock 7 | build/* -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // 使用 IntelliSense 了解相关属性。 3 | // 悬停以查看现有属性的描述。 4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Dart", 9 | "program": "example/example.dart", 10 | "request": "launch", 11 | "type": "dart" 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## [v0.2.1+1] SEPT-06-2021 4 | updated libraries to support latest Flutter version 5 | Applied Fix to Zilliqa Class since it was not receiving Network object 6 | 7 | ## [v0.2.0] JUNE-04-2021 8 | updated to null safety 9 | 10 | ## [v0.1.20-dev.2] NOV-20-2020 11 | update protobuf to v1.1.0 12 | ## [v0.1.20-dev.1] NOV-16-2020 13 | fix typo and update uuid 14 | ## [v0.1.19] AUG-21-2019 15 | 16 | 17 | 18 | ## Feb-20-2019 19 | 20 | - added `isolates` to account.encryptAccount/decryptAccount 21 | - replace `angel_validate` to `validators` 22 | 23 | ## Jan-19-2019 24 | 25 | - `dartfmt` the proto package 26 | - edit `testScilla.dart`, eliminate the `==` type warning 27 | - update description 28 | - move `example.dart` to example folder 29 | 30 | ## Jan-18-2019 31 | 32 | initial added to pub 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2018 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Laksa, Dart Version 2 | 3 | Waiting for Usage example and docs 4 | 5 | ## Porting from Laksa and ZilliqaJS 6 | 7 | - Account 8 | 9 | - [x] create 10 | - [x] toFile 11 | - [x] fromFile 12 | - [x] signTransaction 13 | 14 | - Wallet 15 | 16 | - [x] add 17 | - [x] remove 18 | - [x] getAccount 19 | - [x] encryptAccount 20 | - [x] decryptAccount 21 | - [x] setDefaultAccount 22 | - [x] getDefaultAccount 23 | 24 | - crypto 25 | 26 | - [x] getDerivedKey ((keystore)) 27 | - [x] encrypt(keystore) 28 | - [x] decrypt(keystore) 29 | - [x] sign(Schnorr function) 30 | - [x] verify(Schnorr function) 31 | - [x] SchnorrSign(Schnorr function with protobuf encodation) 32 | - [x] generatePrivateKey 33 | - [x] getPublicKeyFromPrivateKey 34 | - [x] getPublicKeyFromPrivateKey 35 | - [x] getAddressFromPublicKey 36 | - [x] getAddressFromPrivateKey 37 | - [x] hmac-drbg(HMAC update digest) 38 | - [x] getDRBG(inner function) 39 | - [x] generateNewPrivateKey(inner function) 40 | - [x] privateKeyToPublic(inner function) 41 | - [x] getPublic(inner function) 42 | 43 | - Messenger 44 | 45 | - [x] send 46 | - [x] sendServer(Scilla runner) 47 | - [x] setNodeProvider 48 | - [x] setScillaProvider 49 | - [x] setMiddleware 50 | - [x] useMiddleware 51 | 52 | - Blockchain(RPC methods) 53 | 54 | - [x] getBalance 55 | - [x] getBlockchainInfo 56 | - [x] getDSBlock 57 | - [x] getTxBlock 58 | - [x] getLatestDSBlock 59 | - [x] getNumDSBlocks 60 | - [x] getDSBlockRate 61 | - [x] getDSBlockListing 62 | - [x] getLatestTxBlock 63 | - [x] getNumTxBlocks 64 | - [x] getTxBlockRate 65 | - [x] getTxBlockListing 66 | - [x] getNumTransactions 67 | - [x] getTransactionRate 68 | - [x] getCurrentMiniEpoch 69 | - [x] getCurrentDSEpoch 70 | - [x] getPrevDifficulty 71 | - [x] getPrevDSDifficulty 72 | - [x] getRecentTransactions 73 | - [x] getNumTxnsTxEpoch 74 | - [x] getNumTxnsDSEpoch 75 | - [x] getMinimumGasPrice 76 | - [x] createTransaction 77 | - [x] checkCode(Scilla runner) 78 | - [x] testCall(Scilla runner) 79 | 80 | - Provider 81 | 82 | - BaseProvider 83 | - HttpProvider 84 | - [x] buildPayload 85 | - [x] buildEndpointPayload 86 | - [x] performRPC 87 | - [x] send 88 | - [x] sendServer(Scilla runner) 89 | - RPCMiddleware 90 | - [x] RPCResponseBody 91 | - [x] SuccessMiddleware 92 | - [x] ErrorMiddleware 93 | - RPCMehod 94 | - Endpoint 95 | 96 | - Transaction 97 | 98 | - [x] Factory 99 | - [x] sendTransaction 100 | - [x] trackTx 101 | - [x] confirm 102 | - [x] getVersion(calculate version number) 103 | 104 | - Contract 105 | 106 | - [x] Factory 107 | - [x] deploy 108 | - [x] call 109 | - [x] confirmTx 110 | - [x] sendContract 111 | - [x] signTxn 112 | - [x] getState 113 | - [x] setInitParamsValues 114 | - [x] setDeployPayload 115 | - [x] setCallPayload 116 | 117 | - utils 118 | - [x] numbers.strip0x 119 | - [x] numbers.toHex 120 | - [x] numbers.bytesToHex 121 | - [x] numbers.numberToBytes 122 | - [x] numbers.hexToBytes 123 | - [x] numbers.intToBytes 124 | - [x] numbers.hexToInt 125 | - [x] validators.isUrl 126 | - [x] validators.isByteString 127 | - [x] validators.isAddres 128 | - [x] validators.isPublicKey 129 | - [x] validators.isPrivateKey 130 | - [x] validators.isSignature 131 | - [x] unit.fromQa 132 | - [x] unit.toQa 133 | - [x] encodeTransactionProto 134 | 135 | ## Thanks to 136 | 137 | - Zilliqa, who make the js lib originally. 138 | - PointyCastle, who make the dart crypto packages. 139 | - Web3Dart, who make the web3 dart version. 140 | -------------------------------------------------------------------------------- /contracts/fungible-token.scilla: -------------------------------------------------------------------------------- 1 | scilla_version 0 2 | 3 | (***************************************************) 4 | (* Associated library *) 5 | (***************************************************) 6 | import IntUtils 7 | library FungibleToken 8 | 9 | let one_msg = 10 | fun (msg : Message) => 11 | let nil_msg = Nil {Message} in 12 | Cons {Message} msg nil_msg 13 | 14 | let two_msgs = 15 | fun (msg1 : Message) => 16 | fun (msg2 : Message) => 17 | let msgs_tmp = one_msg msg2 in 18 | Cons {Message} msg1 msgs_tmp 19 | 20 | (* Error events *) 21 | type Error = 22 | | CodeIsSender 23 | | CodeInsufficientFunds 24 | | CodeInsufficientAllowance 25 | 26 | let make_error = 27 | fun (result : Error) => 28 | let result_code = 29 | match result with 30 | | CodeIsSender => Int32 -1 31 | | CodeInsufficientFunds => Int32 -2 32 | | CodeInsufficientAllowance => Int32 -3 33 | end 34 | in 35 | { _exception : "Error"; code : result_code } 36 | 37 | let zero = Uint128 0 38 | 39 | (* Dummy user-defined ADT *) 40 | type Unit = 41 | | Unit 42 | 43 | let get_val = 44 | fun (some_val: Option Uint128) => 45 | match some_val with 46 | | Some val => val 47 | | None => zero 48 | end 49 | 50 | (***************************************************) 51 | (* The contract definition *) 52 | (***************************************************) 53 | 54 | contract FungibleToken 55 | ( 56 | contract_owner: ByStr20, 57 | name : String, 58 | symbol: String, 59 | decimals: Uint32, 60 | init_supply : Uint128 61 | ) 62 | 63 | (* Mutable fields *) 64 | 65 | field total_supply : Uint128 = init_supply 66 | 67 | field balances: Map ByStr20 Uint128 68 | = let emp_map = Emp ByStr20 Uint128 in 69 | builtin put emp_map contract_owner init_supply 70 | 71 | field allowances: Map ByStr20 (Map ByStr20 Uint128) 72 | = Emp ByStr20 (Map ByStr20 Uint128) 73 | 74 | (**************************************) 75 | (* Procedures *) 76 | (**************************************) 77 | 78 | procedure ThrowError(err : Error) 79 | e = make_error err; 80 | throw e 81 | end 82 | 83 | procedure IsNotSender(address: ByStr20) 84 | is_sender = builtin eq _sender address; 85 | match is_sender with 86 | | True => 87 | err = CodeIsSender; 88 | ThrowError err 89 | | False => 90 | end 91 | end 92 | 93 | procedure AuthorizedMoveIfSufficientBalance(from: ByStr20, to: ByStr20, amount: Uint128) 94 | o_from_bal <- balances[from]; 95 | bal = get_val o_from_bal; 96 | can_do = uint128_le amount bal; 97 | match can_do with 98 | | True => 99 | (* Subtract amount from from and add it to to address *) 100 | new_from_bal = builtin sub bal amount; 101 | balances[from] := new_from_bal; 102 | (* Adds amount to to address *) 103 | get_to_bal <- balances[to]; 104 | new_to_bal = match get_to_bal with 105 | | Some bal => builtin add bal amount 106 | | None => amount 107 | end; 108 | balances[to] := new_to_bal 109 | | False => 110 | (* Balance not sufficient *) 111 | err = CodeInsufficientFunds; 112 | ThrowError err 113 | end 114 | end 115 | 116 | (***************************************) 117 | (* Transitions *) 118 | (***************************************) 119 | 120 | (* @dev: Increase the allowance of an approved_spender over the caller tokens. Only token_owner allowed to invoke. *) 121 | (* param spender: Address of the designated approved_spender. *) 122 | (* param amount: Number of tokens to be increased as allowance for the approved_spender. *) 123 | transition IncreaseAllowance(spender: ByStr20, amount: Uint128) 124 | IsNotSender spender; 125 | some_current_allowance <- allowances[_sender][spender]; 126 | current_allowance = get_val some_current_allowance; 127 | new_allowance = builtin add current_allowance amount; 128 | allowances[_sender][spender] := new_allowance; 129 | e = {_eventname : "IncreasedAllowance"; token_owner : _sender; spender: spender; new_allowance : new_allowance}; 130 | event e 131 | end 132 | 133 | (* @dev: Decrease the allowance of an approved_spender over the caller tokens. Only token_owner allowed to invoke. *) 134 | (* param spender: Address of the designated approved_spender. *) 135 | (* param amount: Number of tokens to be decreased as allowance for the approved_spender. *) 136 | transition DecreaseAllowance(spender: ByStr20, amount: Uint128) 137 | IsNotSender spender; 138 | some_current_allowance <- allowances[_sender][spender]; 139 | current_allowance = get_val some_current_allowance; 140 | new_allowance = 141 | let amount_le_allowance = uint128_le amount current_allowance in 142 | match amount_le_allowance with 143 | | True => builtin sub current_allowance amount 144 | | False => zero 145 | end; 146 | allowances[_sender][spender] := new_allowance; 147 | e = {_eventname : "DecreasedAllowance"; token_owner : _sender; spender: spender; new_allowance : new_allowance}; 148 | event e 149 | end 150 | 151 | (* @dev: Moves an amount tokens from _sender to the recipient. Used by token_owner. *) 152 | (* @dev: Balance of recipient will increase. Balance of _sender will decrease. *) 153 | (* @param to: Address of the recipient whose balance is increased. *) 154 | (* @param amount: Amount of tokens to be sent. *) 155 | transition Transfer(to: ByStr20, amount: Uint128) 156 | AuthorizedMoveIfSufficientBalance _sender to amount; 157 | e = {_eventname : "TransferSuccess"; sender : _sender; recipient : to; amount : amount}; 158 | event e; 159 | (* Prevent sending to a contract address that does not support transfers of token *) 160 | msg_to_recipient = {_tag : "RecipientAcceptTransfer"; _recipient : to; _amount : zero; 161 | sender : _sender; recipient : to; amount : amount}; 162 | msg_to_sender = {_tag : "TransferSuccessCallBack"; _recipient : _sender; _amount : zero; 163 | sender : _sender; recipient : to; amount : amount}; 164 | msgs = two_msgs msg_to_recipient msg_to_sender; 165 | send msgs 166 | end 167 | 168 | (* @dev: Move a given amount of tokens from one address to another using the allowance mechanism. The caller must be an approved_spender. *) 169 | (* @dev: Balance of recipient will increase. Balance of token_owner will decrease. *) 170 | (* @param from: Address of the token_owner whose balance is decreased. *) 171 | (* @param to: Address of the recipient whose balance is increased. *) 172 | (* @param amount: Amount of tokens to be transferred. *) 173 | transition TransferFrom(from: ByStr20, to: ByStr20, amount: Uint128) 174 | o_spender_allowed <- allowances[from][_sender]; 175 | allowed = get_val o_spender_allowed; 176 | can_do = uint128_le amount allowed; 177 | match can_do with 178 | | True => 179 | AuthorizedMoveIfSufficientBalance from to amount; 180 | e = {_eventname : "TransferFromSuccess"; initiator : _sender; sender : from; recipient : to; amount : amount}; 181 | event e; 182 | new_allowed = builtin sub allowed amount; 183 | allowances[from][_sender] := new_allowed; 184 | (* Prevent sending to a contract address that does not support transfers of token *) 185 | msg_to_recipient = {_tag: "RecipientAcceptTransferFrom"; _recipient : to; _amount: zero; 186 | initiator: _sender; sender : from; recipient: to; amount: amount}; 187 | msg_to_sender = {_tag: "TransferFromSuccessCallBack"; _recipient: _sender; _amount: zero; 188 | initiator: _sender; sender: from; recipient: to; amount: amount}; 189 | msgs = two_msgs msg_to_recipient msg_to_sender; 190 | send msgs 191 | | False => 192 | err = CodeInsufficientAllowance; 193 | ThrowError err 194 | end 195 | end -------------------------------------------------------------------------------- /contracts/hello_world.scilla: -------------------------------------------------------------------------------- 1 | (* HelloWorld contract *) 2 | 3 | (***************************************************) 4 | (* Scilla version *) 5 | (***************************************************) 6 | 7 | scilla_version 0 8 | 9 | (***************************************************) 10 | (* Associated library *) 11 | (***************************************************) 12 | library HelloWorld 13 | 14 | let not_owner_code = Uint32 1 15 | let set_hello_code = Uint32 2 16 | 17 | (***************************************************) 18 | (* The contract definition *) 19 | (***************************************************) 20 | 21 | contract HelloWorld 22 | (owner: ByStr20) 23 | 24 | field welcome_msg : String = "" 25 | 26 | transition setHello (msg : String) 27 | is_owner = builtin eq owner _sender; 28 | match is_owner with 29 | | False => 30 | e = {_eventname : "setHello"; code : not_owner_code}; 31 | event e 32 | | True => 33 | welcome_msg := msg; 34 | e = {_eventname : "setHello"; code : set_hello_code}; 35 | event e 36 | end 37 | end 38 | 39 | transition getHello () 40 | r <- welcome_msg; 41 | e = {_eventname: "getHello"; msg: r}; 42 | event e 43 | end 44 | -------------------------------------------------------------------------------- /contracts/helloworld.txt: -------------------------------------------------------------------------------- 1 | (* scilla_version 0 *) 2 | (* HelloWorld contract *) 3 | import ListUtils 4 | (***************************************************) 5 | (* Associated library *) 6 | (***************************************************) 7 | library HelloWorld 8 | 9 | let one_msg = 10 | fun (msg : Message) => 11 | let nil_msg = Nil {Message} in 12 | Cons {Message} msg nil_msg 13 | 14 | let not_owner_code = Int32 1 15 | let set_hello_code = Int32 2 16 | 17 | (***************************************************) 18 | (* The contract definition *) 19 | (***************************************************) 20 | 21 | contract HelloWorld 22 | (owner: ByStr20) 23 | 24 | field welcome_msg : String = "" 25 | 26 | transition setHello (msg : String) 27 | is_owner = builtin eq owner _sender; 28 | match is_owner with 29 | | False => 30 | msg = {_tag : "Main"; _recipient : _sender; _amount : Uint128 0; code : not_owner_code}; 31 | msgs = one_msg msg; 32 | send msgs 33 | | True => 34 | welcome_msg := msg; 35 | msg = {_tag : "Main"; _recipient : _sender; _amount : Uint128 0; code : set_hello_code}; 36 | msgs = one_msg msg; 37 | send msgs 38 | end 39 | end 40 | 41 | transition getHello () 42 | r <- welcome_msg; 43 | msg = {_tag : "Main"; _recipient : _sender; _amount : Uint128 0; msg : r}; 44 | msgs = one_msg msg; 45 | send msgs 46 | end -------------------------------------------------------------------------------- /contracts/helloworldversion.txt: -------------------------------------------------------------------------------- 1 | scilla_version 0 2 | (* Deployed by neeboo@FireStack.one *) 3 | (* HelloWorld contract *) 4 | 5 | import ListUtils 6 | 7 | (***************************************************) 8 | (* Associated library *) 9 | (***************************************************) 10 | library HelloWorld 11 | 12 | let not_owner_code = Int32 1 13 | let set_hello_code = Int32 2 14 | 15 | (***************************************************) 16 | (* The contract definition *) 17 | (***************************************************) 18 | 19 | contract HelloWorld 20 | (owner: ByStr20) 21 | 22 | field welcome_msg : String = "" 23 | 24 | transition setHello (msg : String) 25 | is_owner = builtin eq owner _sender; 26 | match is_owner with 27 | | False => 28 | e = {_eventname : "setHello()"; code : not_owner_code}; 29 | event e 30 | | True => 31 | welcome_msg := msg; 32 | e = {_eventname : "setHello()"; code : set_hello_code}; 33 | event e 34 | end 35 | end 36 | 37 | 38 | transition getHello () 39 | r <- welcome_msg; 40 | e = {_eventname: "getHello()"; msg: r}; 41 | event e 42 | end -------------------------------------------------------------------------------- /example/example.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | import 'package:laksadart/src/utils/network.dart'; 4 | import 'package:laksadart/src/utils/unit.dart' as unit; 5 | import 'package:laksadart/laksadart.dart'; 6 | import 'package:laksadart/src/utils/numbers.dart' as numbers; 7 | 8 | main() async { 9 | var zilliqa = new Zilliqa(network: zilliqaNetworks["dev"]); 10 | wallet(zilliqa); 11 | var account = zilliqa.wallet 12 | .add('e19d05c5452598e24caad4a0d85a49146f7be089515c905ae6a19e8a578a6930'); 13 | 14 | autoTransaction(account, zilliqa); 15 | deploy(); 16 | } 17 | 18 | void wallet(Zilliqa zilliqa) async { 19 | print("Creating a New Wallet"); 20 | var newAcc = zilliqa.wallet.create(); 21 | await newAcc.encryptAccount('passphrase'); 22 | print("New Wallet Bech32 Address: ${newAcc.address!.bech32}"); 23 | await newAcc.updateBalance(); 24 | print("New Wallet Balance ${newAcc.balance}"); 25 | } 26 | 27 | void autoTransaction(Account? acc, Zilliqa zilliqa) async { 28 | print("Auto Transaction"); 29 | await acc!.encryptAccount('111'); 30 | await acc.updateBalance(); 31 | var nonce = acc.nonce!; 32 | var txn = zilliqa.transactions.newTx({ 33 | 'toAddr': ZilAddress('2e3c9b415b19ae4035503a06192a0fad76e04243').bech32, 34 | 'amount': unit.Unit.Li(nonce + 1).qa, 35 | 'gasPrice': unit.Unit.Li(2000).qa, 36 | 'gasLimit': 50, 37 | 'version': numbers.pack(zilliqa.network!.chainID!, 1), 38 | }); 39 | 40 | var signed = await acc.signTransaction(txn, passphrase: '111'); 41 | print("Transaction Payload: ${json.encode(signed.toPayload)}"); 42 | var sent = await signed.sendTransaction(); 43 | if (sent == null) { 44 | print("sent txn is null"); 45 | return; 46 | } 47 | print("Transaction ID ${sent.transaction.transactionID}"); 48 | var sendTime = DateTime.now(); 49 | print("Transaction is confirmed?"); 50 | var result = 51 | await sent.transaction.confirm(txHash: sent.transaction.transactionID); 52 | print(result.getReceiptInfo(zilliqa.network!.blockExplorerNetwork!)); 53 | var during = DateTime.now().difference(sendTime); 54 | print('Transaction Confirmed: $during'); 55 | } 56 | 57 | void deploy() async { 58 | print("Deploy Contract"); 59 | File contract = new File('contracts/hello_world.scilla'); 60 | 61 | await contract.readAsString().then((String? contractString) async { 62 | Zilliqa zilliqa = new Zilliqa(nodeUrl: 'https://dev-api.zilliqa.com'); 63 | var init = [ 64 | {"vname": "_scilla_version", "type": "Uint32", "value": "0"}, 65 | { 66 | "vname": "owner", 67 | "type": "ByStr20", 68 | "value": "0x9bfec715a6bd658fcb62b0f8cc9bfa2ade71434a" 69 | }, 70 | ]; 71 | 72 | zilliqa.wallet.add( 73 | 'e19d05c5452598e24caad4a0d85a49146f7be089515c905ae6a19e8a578a6930'); 74 | if (contractString != null) { 75 | return; 76 | } 77 | var newContract = zilliqa.contracts.newContract( 78 | code: contractString!, 79 | init: init, 80 | version: numbers.pack(zilliqa.network!.chainID!, 1)); 81 | newContract.setDeployPayload( 82 | gasLimit: 1225, gasPrice: BigInt.from(3000000000), toDS: false); 83 | var sent = await newContract.sendContract(); 84 | print("Sent Transaction Payload: ${sent.transaction!.toPayload}"); 85 | var sendTime = DateTime.now(); 86 | print('Sent Contract at: $sendTime'); 87 | print("Sent Transaction ID: ${sent.transaction!.transactionID}"); 88 | var deployed = await sent.confirmTx(); 89 | print("Deployed Contract Status ${deployed.status}"); 90 | print("Deployed Contract Address: ${deployed.contractAddress}"); 91 | var during = DateTime.now().difference(sendTime); 92 | print('Deployed Confirmed: $during'); 93 | 94 | // test call contract 95 | 96 | deployed.setCallPayload( 97 | transition: 'setHello', 98 | params: [ 99 | {'vname': "msg", 'type': "String", 'value': "Hello Worldz!"} 100 | ], 101 | gasLimit: 10000, 102 | gasPrice: unit.Unit.Li(2000).qa, 103 | amount: unit.Unit.Qa(0).qa, 104 | toDS: false); 105 | var sentCall = await deployed.sendContract(); 106 | print("Sent Call Transaction Payload: ${sentCall.transaction!.toPayload}"); 107 | var calledTime = DateTime.now(); 108 | print('Sent Call Time: $calledTime'); 109 | print("Sent Call Transaction ID: ${sentCall.transaction!.transactionID}"); 110 | 111 | var result = await sentCall.confirmTx(); 112 | print("Called Contract Status: ${result.status}"); 113 | print("Called Contract Address: ${sentCall.contractAddress}"); 114 | var during2 = DateTime.now().difference(calledTime); 115 | var state = await sentCall.getState(); 116 | var message = state!.resultMap!["welcome_msg"]; 117 | print("Called Contract State: $message"); 118 | print('Called Confirmed: $during2'); 119 | //'https://scilla-runner.zilliqa.com' 120 | }); 121 | } 122 | -------------------------------------------------------------------------------- /lib/laksadart.dart: -------------------------------------------------------------------------------- 1 | library laksadart; 2 | 3 | export 'src/zilliqa.dart'; 4 | export './src/account/index.dart'; 5 | export './src/contract/index.dart'; 6 | export './src/transaction/index.dart'; 7 | export './src/core/index.dart'; 8 | export './src/crypto/index.dart'; 9 | export './src/provider/index.dart'; 10 | export './src/messenger/index.dart'; 11 | export './src/utils/index.dart'; 12 | -------------------------------------------------------------------------------- /lib/src/account/account.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:async'; 3 | import 'package:laksadart/src/crypto/schnorr.dart' as crypto; 4 | import 'package:laksadart/src/crypto/isolates.dart'; 5 | import 'package:laksadart/src/crypto/keystore/api.dart'; 6 | import 'package:laksadart/src/utils/numbers.dart' as numbers; 7 | import 'package:laksadart/src/core/zilliqa_module.dart'; 8 | import 'package:laksadart/src/messenger/messenger.dart'; 9 | import 'package:laksadart/src/provider/Middleware.dart'; 10 | import 'package:laksadart/src/provider/net.dart'; 11 | import 'package:laksadart/src/transaction/transaction.dart'; 12 | import './api.dart' show BaseAccount; 13 | import './address.dart'; 14 | 15 | /// Account instance with keystore encrypt/decrypt 16 | class Account implements BaseAccount, KeyStore, ZilliqaModule, AccountState { 17 | /// static privatekey checker 18 | static final RegExp isPrivateKey = 19 | new RegExp(r"^(0x)?[0-9a-f]{64}", caseSensitive: false); 20 | 21 | Messenger? _messenger; 22 | 23 | String? privateKey; 24 | String? publicKey; 25 | ZilAddress? address; 26 | String? balance = '0'; 27 | int? nonce = 0; 28 | 29 | bool? isFound; 30 | 31 | /// transalte privateKey to Big Int 32 | BigInt? get privateKeyBigInt => Account._privateKeyToBigInt(this.privateKey); 33 | 34 | /// transate privatekey to Bytes 35 | List? get privateKeyBytes => Account._privateKeyToBytes(this.privateKey); 36 | 37 | /// get account Map 38 | Map get accountMap => this.toMap(); 39 | 40 | /// get keystore Map 41 | Map? get keyStoreMap => this.getKeyStore(); 42 | 43 | /// account encryption checker 44 | bool get isEncrypted => this.checkEncrypted(); 45 | 46 | /// constructor 47 | Account([String? privateKey, Messenger? messenger]) { 48 | this.privateKey = privateKey; 49 | this.publicKey = this.getPublicKey(privateKey); 50 | this.address = this.getAddress(privateKey); 51 | this.messenger = messenger; 52 | } 53 | 54 | static fromMap(Map accountMap) { 55 | final prv = accountMap['privateKey']; 56 | final pub = accountMap['publicKey']; 57 | final addr = ZilAddress.fromAddress(accountMap['address']); 58 | Account newAcc = new Account(); 59 | newAcc.privateKey = prv; 60 | newAcc.publicKey = pub; 61 | newAcc.address = addr; 62 | return newAcc; 63 | } 64 | 65 | static fromFile(String keyStore, String? passphrase) async { 66 | // String newPrvKey = await decrypt(json.decode(keyStore), passphrase); 67 | try { 68 | String? newPrvKey = await asyncDecrypt(json.decode(keyStore), passphrase); 69 | return new Account(newPrvKey); 70 | } catch (e) { 71 | rethrow; 72 | } 73 | } 74 | 75 | Future toFile(String? passphrase, 76 | [Map? options]) async { 77 | try { 78 | await this.encryptAccount(passphrase, options); 79 | return json.encode(this.keyStoreMap); 80 | } catch (e) { 81 | rethrow; 82 | } 83 | } 84 | 85 | /// create method, should call constructor first 86 | Account create() { 87 | try { 88 | String prv = crypto.generatePrivateKey(); 89 | return new Account(prv, this.messenger); 90 | } catch (e) { 91 | rethrow; 92 | } 93 | } 94 | 95 | Future asyncCreate() async { 96 | try { 97 | String? prv = await asyncGeneratePrivateKey(); 98 | return new Account(prv, this.messenger); 99 | } catch (e) { 100 | rethrow; 101 | } 102 | } 103 | 104 | @override 105 | set messenger(Messenger? messenger) => this._messenger = messenger; 106 | 107 | @override 108 | Messenger get messenger => this._messenger!; 109 | 110 | /// import account from hex string 111 | Account import(dynamic prvHex) { 112 | if (prvHex is String) { 113 | return Account._importFromString(prvHex); 114 | } else if (prvHex is BigInt) { 115 | return Account._importFromBigInt(prvHex); 116 | } else { 117 | return new Account(); 118 | } 119 | } 120 | 121 | /// to map method 122 | Map toMap() { 123 | return { 124 | 'privateKey': this.privateKey, 125 | 'publicKey': this.publicKey, 126 | 'address': this.address!.checkSumAddress.toString() 127 | }; 128 | } 129 | 130 | /// to json method 131 | String toJson() { 132 | return json.encode(this.accountMap); 133 | } 134 | 135 | /// account encryption 136 | Future encryptAccount(String? passphrase, 137 | [Map? options]) async { 138 | if (this.privateKey is String && 139 | Account.isPrivateKey.hasMatch(this.privateKey!)) { 140 | // this.privateKey = await encrypt(this.privateKey, passphrase, options); 141 | this.privateKey = 142 | await asyncEncrypt(this.privateKey, passphrase, options); 143 | } else { 144 | return null; 145 | } 146 | } 147 | 148 | /// account decyption 149 | Future decryptAccount(String? passphrase) async { 150 | if (this.privateKey is String && 151 | Account.isPrivateKey.hasMatch(this.privateKey!)) { 152 | return null; 153 | } 154 | // this.privateKey = await decrypt(json.decode(this.privateKey), passphrase); 155 | this.privateKey = 156 | await asyncDecrypt(json.decode(this.privateKey!), passphrase); 157 | 158 | // return this; 159 | } 160 | 161 | Future updateBalance() async { 162 | RPCResponseBody res = await this 163 | .messenger 164 | .send(RPCMethod.GetBalance, this.address.toString()); 165 | if (res.error == null && res.result != null) { 166 | var balanceMap = res.result!.toMap(); 167 | if (balanceMap != null) { 168 | this.balance = balanceMap['balance']; 169 | this.nonce = balanceMap['nonce']; 170 | this.isFound = true; 171 | } 172 | } else { 173 | this.balance = '0'; 174 | this.nonce = 0; 175 | this.isFound = false; 176 | } 177 | } 178 | 179 | /// keystore getter 180 | Map? getKeyStore() { 181 | if (this.privateKey is String && 182 | Account.isPrivateKey.hasMatch(this.privateKey!)) { 183 | return null; 184 | } 185 | return json.decode(this.privateKey!); 186 | } 187 | 188 | /// private key to big int 189 | static BigInt? _privateKeyToBigInt(String? prv) { 190 | try { 191 | if (prv is String && Account.isPrivateKey.hasMatch(prv)) { 192 | return numbers.hexToInt(prv); 193 | } else { 194 | return null; 195 | } 196 | } catch (e) { 197 | rethrow; 198 | } 199 | } 200 | 201 | /// privat key to bytes 202 | static List? _privateKeyToBytes(String? prv) { 203 | try { 204 | if (prv is String && Account.isPrivateKey.hasMatch(prv)) { 205 | return numbers.hexToBytes(prv); 206 | } else { 207 | return null; 208 | } 209 | } catch (e) { 210 | rethrow; 211 | } 212 | } 213 | 214 | /// import account from hex 215 | static Account _importFromString(String prvHex) { 216 | try { 217 | if (Account.isPrivateKey.hasMatch(prvHex)) { 218 | return new Account(prvHex); 219 | } else { 220 | throw ArgumentError('PrivateKey is not correct'); 221 | } 222 | } catch (e) { 223 | rethrow; 224 | } 225 | } 226 | 227 | /// import account from BigInt 228 | static Account _importFromBigInt(BigInt number) { 229 | try { 230 | String prvHex = number.toRadixString(16); 231 | return Account._importFromString(prvHex); 232 | } catch (e) { 233 | rethrow; 234 | } 235 | } 236 | 237 | /// get publicKey from privatKey 238 | String? getPublicKey(String? privateKey) { 239 | if (privateKey != null && Account.isPrivateKey.hasMatch(privateKey)) { 240 | return crypto.getPubKeyFromPrivateKey(privateKey); 241 | } else { 242 | return null; 243 | } 244 | } 245 | 246 | Future asyncGetPublicKey(String? privateKey) async { 247 | if (privateKey != null && Account.isPrivateKey.hasMatch(privateKey)) { 248 | return await asyncGetPubKeyFromPrivateKey(privateKey); 249 | } else { 250 | return null; 251 | } 252 | } 253 | 254 | /// get address key from privateKey 255 | ZilAddress? getAddress(String? privateKey) { 256 | if (privateKey != null && Account.isPrivateKey.hasMatch(privateKey)) { 257 | return ZilAddress.fromPrivateKey(privateKey); 258 | } else { 259 | return null; 260 | } 261 | } 262 | 263 | Future asyncGetAddress(String? privateKey) async { 264 | if (privateKey != null && Account.isPrivateKey.hasMatch(privateKey)) { 265 | return await ZilAddress.asyncFromPrivateKey(privateKey); 266 | } else { 267 | return null; 268 | } 269 | } 270 | 271 | /// encryption checker 272 | bool checkEncrypted() { 273 | if (this.privateKey == null) { 274 | throw 'Account is corrupted'; 275 | } 276 | Map? store = this.getKeyStore(); 277 | if (store is Map && store['crypto'] != null) { 278 | return true; 279 | } else 280 | return false; 281 | } 282 | 283 | /// sign transasction 284 | Future signTransaction(Transaction? txnObj, 285 | {String? passphrase, Map? options}) async { 286 | if (this.isEncrypted) { 287 | await this.decryptAccount(passphrase); 288 | await this.updateBalance(); 289 | Map newTxMap = Map.from(txnObj!.txParams); 290 | newTxMap.update('nonce', (found) => this.nonce! + 1, 291 | ifAbsent: () => this.nonce! + 1); 292 | // var signed = crypto.SchnorrSign(this.privateKey, txnObj.txParams); 293 | var signed = await asyncSchnorrSign(this.privateKey, newTxMap); 294 | await this.encryptAccount(passphrase, options ?? {'level': 1024}); 295 | return txnObj.map((Map obj) { 296 | obj.addAll(signed); 297 | return obj; 298 | }); 299 | } else { 300 | await this.updateBalance(); 301 | 302 | Map newTxMap = Map.from(txnObj!.txParams); 303 | 304 | newTxMap.update('nonce', (found) => this.nonce! + 1, 305 | ifAbsent: () => this.nonce! + 1); 306 | // var signed = crypto.SchnorrSign(this.privateKey, newTxMap); 307 | var signed = await asyncSchnorrSign(this.privateKey, newTxMap); 308 | return txnObj.map((Map obj) { 309 | obj.addAll(signed); 310 | return obj; 311 | }); 312 | } 313 | } 314 | } 315 | -------------------------------------------------------------------------------- /lib/src/account/address.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:meta/meta.dart'; 3 | 4 | import 'package:laksadart/src/crypto/schnorr.dart' as crypto; 5 | import 'package:laksadart/src/crypto/isolates.dart'; 6 | import 'package:laksadart/src/crypto/bech32.dart'; 7 | import 'package:laksadart/src/crypto/checksum.dart'; 8 | import 'package:laksadart/src/utils/numbers.dart' as numbers; 9 | import 'package:laksadart/src/utils/validators.dart' as validator; 10 | 11 | @immutable 12 | class Address { 13 | static final RegExp basicAddress = 14 | new RegExp(r"^(0x)?[0-9a-f]{40}", caseSensitive: false); 15 | 16 | static const int _addLenBytes = 20; 17 | static final BigInt biggestAddress = 18 | (BigInt.one << (_addLenBytes * 8)) - BigInt.one; 19 | 20 | /// The number associated with the address 21 | final BigInt number; 22 | // final String str; 23 | Address(String hex) : this.fromNumber(_hexToAddressNum(hex)); 24 | 25 | /// Creates an address from its number 26 | Address.fromNumber(this.number) { 27 | if (number.isNegative) { 28 | throw new ArgumentError("Addresses must be positive"); 29 | } 30 | if (number > biggestAddress) { 31 | throw new ArgumentError("Addresses must fit in $_addLenBytes bytes"); 32 | } 33 | } 34 | 35 | /// Returns this address in a hexadecimal representation, with 0x prefixed. 36 | String get hex => numbers.toHex(number, 37 | pad: true, forcePadLen: _addLenBytes * 2, include0x: true); 38 | 39 | /// Returns this address in a hexadecimal representation, without any prefix 40 | String get address => numbers.toHex(number, 41 | pad: true, forcePadLen: _addLenBytes * 2, include0x: false); 42 | 43 | @override 44 | String toString() => hex; 45 | 46 | static BigInt _hexToAddressNum(String hex) { 47 | try { 48 | if (!basicAddress.hasMatch(hex)) { 49 | throw new ArgumentError("Not a valid Address (needs to be " 50 | "hexadecimal, 20 bytes and optionally prefixed with 0x): $hex"); 51 | } 52 | if (hex.toUpperCase() == hex || hex.toLowerCase() == hex) { 53 | return numbers.hexToInt(hex); 54 | } 55 | return numbers.hexToInt(hex); 56 | } catch (e) { 57 | throw e.toString(); 58 | } 59 | 60 | // Either all lower or upper case => valid address, parse 61 | } 62 | } 63 | 64 | enum AddressType { CheckSum, Bech32, Bytes20, Bytes20Hex, Invalid } 65 | 66 | /// extends the Address to Zillia Standard 67 | /// 68 | /// 69 | class ZilAddress extends Address { 70 | String get hexAddress => this._getHexAddress(); 71 | BigInt get bnAddress => this._getBigIntAddress(); 72 | List get byteAddress => this.toByteAddress(this.address); 73 | String get checkSumAddress => this.toCheckSumAddress(this.address); 74 | String get bech32 => toBech32Address(this.checkSumAddress); 75 | 76 | String get bytes20Hex => this.toBytes20Hex(this.address); 77 | bool get isValid => this.isAddress(this.address); 78 | 79 | String get address => super.address; 80 | 81 | /// constructor super from Address 82 | ZilAddress(String str) : super(str); 83 | 84 | @override 85 | String toString() => address; 86 | 87 | BigInt _getBigIntAddress() { 88 | return this.getBigIntAddress(this.address); 89 | } 90 | 91 | String _getHexAddress() { 92 | return '0x' + this.address; 93 | } 94 | 95 | BigInt getBigIntAddress(str) { 96 | return Address._hexToAddressNum(str); 97 | } 98 | 99 | /// check if address is valid 100 | bool isAddress(str) { 101 | RegExp superReg = Address.basicAddress; 102 | return superReg.hasMatch(str); 103 | } 104 | 105 | /// convert address to checkSumAddress 106 | String toCheckSumAddress(String address) { 107 | return toChecksum(address); 108 | } 109 | 110 | String toBytes20Hex(String address) { 111 | return this.toCheckSumAddress(address).toLowerCase(); 112 | } 113 | 114 | /// conver hex string to Byte 115 | List toByteAddress(String address) { 116 | String stripAddress = numbers.strip0x(address.toLowerCase()); 117 | return numbers.hexToBytes(stripAddress); 118 | } 119 | 120 | static String toCheckSum(String address) { 121 | return toChecksum(address); 122 | } 123 | 124 | static ZilAddress fromPrivateKey(String privateKey) => 125 | new ZilAddress(crypto.getAddressFromPrivateKey(privateKey)); 126 | 127 | static ZilAddress fromPublicKey(String publicKey) => 128 | new ZilAddress(crypto.getAddressFromPublicKey(publicKey)); 129 | 130 | static Future asyncFromPrivateKey(String privateKey) async { 131 | return new ZilAddress(await asyncGetAddressFromPrivateKey(privateKey)); 132 | } 133 | 134 | static Future asyncFromPublicKey(String publicKey) async { 135 | return new ZilAddress(await asyncGetAddressFromPublicKey(publicKey)); 136 | } 137 | 138 | static ZilAddress fromAddress(String address) => 139 | new ZilAddress(address.toLowerCase()); 140 | 141 | static ZilAddress fromBigInt(BigInt number) => 142 | new ZilAddress(new Address.fromNumber(number).address); 143 | 144 | static AddressType getAddressType(String raw) { 145 | if (validator.isAddress(raw) && validator.isValidChecksumAddress(raw)) { 146 | return AddressType.CheckSum; 147 | } else if (validator.isAddress(raw) && 148 | !validator.isValidChecksumAddress(raw) && 149 | raw.startsWith('0x')) { 150 | return AddressType.Bytes20Hex; 151 | } else if (validator.isAddress(raw) && 152 | !validator.isValidChecksumAddress(raw) && 153 | !raw.startsWith('0x')) { 154 | return AddressType.Bytes20; 155 | } else if (validator.isBech32(raw) && 156 | validator.isValidChecksumAddress(fromBech32Address(raw))) { 157 | return AddressType.Bech32; 158 | } else { 159 | return AddressType.Invalid; 160 | } 161 | } 162 | 163 | static toValidAddress(String addr) { 164 | if (getAddressType(addr) == AddressType.CheckSum) { 165 | return addr; 166 | } else if (getAddressType(addr) == AddressType.Bech32) { 167 | return toChecksum(fromBech32Address(addr)); 168 | } else { 169 | throw 'Invalid address'; 170 | } 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /lib/src/account/api.dart: -------------------------------------------------------------------------------- 1 | abstract class BaseAccount { 2 | String? privateKey; 3 | } 4 | 5 | abstract class BaseWallet { 6 | E get accounts; 7 | int get length; 8 | String? defaultAccount; 9 | } 10 | 11 | abstract class Signer {} 12 | -------------------------------------------------------------------------------- /lib/src/account/index.dart: -------------------------------------------------------------------------------- 1 | library account; 2 | 3 | export 'account.dart'; 4 | export 'wallet.dart'; 5 | export 'address.dart'; 6 | export 'api.dart'; 7 | -------------------------------------------------------------------------------- /lib/src/account/wallet.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:laksadart/src/core/zilliqa_module.dart'; 3 | import 'package:laksadart/src/messenger/messenger.dart'; 4 | import 'package:laksadart/src/utils/numbers.dart' as numbers; 5 | import 'package:bip39/bip39.dart' as bip39; 6 | import 'package:bip32/bip32.dart' as bip32; 7 | import './api.dart' show BaseWallet; 8 | import './account.dart'; 9 | 10 | class Wallet implements BaseWallet>, ZilliqaModule { 11 | List get accounts => List.from(this.toMap.keys); 12 | int get length => accounts.length; 13 | String? defaultAccount; 14 | Messenger? _messenger; 15 | Map toMap = new Map(); 16 | 17 | Wallet(this._messenger); 18 | 19 | @override 20 | void set messenger(Messenger? messenger) => this._messenger = messenger; 21 | 22 | @override 23 | Messenger get messenger => this._messenger!; 24 | 25 | // add to wallet 26 | Account? add(dynamic obj) { 27 | if (obj is Account) { 28 | obj.messenger = this.messenger; 29 | MapEntry entry = 30 | new MapEntry(obj.address.toString(), obj); 31 | this.toMap.addEntries([entry]); 32 | this.getDefaultAccount(); 33 | return this.getAccount(obj.address.toString()); 34 | } else if (obj is String) { 35 | Account acc = new Account(obj, this.messenger); 36 | String address = acc.address.toString(); 37 | MapEntry entryNew = new MapEntry(address, acc); 38 | this.toMap.addEntries([entryNew]); 39 | this.getDefaultAccount(); 40 | return this.getAccount(address.toString()); 41 | } else if (obj is Map) { 42 | Account acc = Account.fromMap(obj as Map); 43 | acc.messenger = this.messenger; 44 | String address = acc.address.toString(); 45 | MapEntry entryNew = new MapEntry(address, acc); 46 | this.toMap.addEntries([entryNew]); 47 | this.getDefaultAccount(); 48 | return this.getAccount(address.toString()); 49 | } else { 50 | return null; 51 | } 52 | } 53 | 54 | Account create() { 55 | var acc = new Account(null, this.messenger).create(); 56 | this.add(acc); 57 | return acc; 58 | } 59 | 60 | Future asyncCreate() async { 61 | var acc = await new Account(null, this.messenger).asyncCreate(); 62 | this.add(acc); 63 | return acc; 64 | } 65 | 66 | void remove(String addr) { 67 | this.toMap.remove(addr); 68 | if (this.defaultAccount == addr) { 69 | this.defaultAccount = null; 70 | } 71 | this.getDefaultAccount(); 72 | } 73 | 74 | void update(String addr, Account? account) { 75 | this.toMap.update(addr, (find) => account); 76 | this.getDefaultAccount(); 77 | } 78 | 79 | Account? getAccount(String? addr) { 80 | return this.toMap[addr!]; 81 | } 82 | 83 | Future encryptAccount(String addr, String passphrase, 84 | [Map? options]) async { 85 | var found = this.getAccount(addr); 86 | if (found == null) return null; 87 | if (found is Account || !found.isEncrypted) { 88 | await found.encryptAccount(passphrase, options); 89 | this.update(addr, found); 90 | return found; 91 | } else { 92 | return null; 93 | } 94 | } 95 | 96 | Future decryptAccount( 97 | String addr, 98 | String passphrase, 99 | ) async { 100 | var found = this.getAccount(addr); 101 | if (found == null) return null; 102 | if (found is Account || found.isEncrypted) { 103 | await found.decryptAccount(passphrase); 104 | this.update(addr, found); 105 | return found; 106 | } else { 107 | return null; 108 | } 109 | } 110 | 111 | Account? getDefaultAccount() { 112 | if (this.length == 0) { 113 | return null; 114 | } 115 | if (this.length == 1) { 116 | this.setDefaultAccount(this.accounts.first); 117 | return this.getAccount(this.defaultAccount); 118 | } 119 | return this.getAccount(this.defaultAccount); 120 | } 121 | 122 | void setDefaultAccount(String addr) { 123 | if (this.getAccount(addr) != null) { 124 | this.defaultAccount = addr; 125 | } 126 | } 127 | 128 | String generateMnemonic() { 129 | return bip39.generateMnemonic(); 130 | } 131 | 132 | bool isValidMnemonic(String phrase) { 133 | var list = phrase.trim().split(" "); 134 | if (list.length < 12) { 135 | return false; 136 | } 137 | return bip39.validateMnemonic(phrase); 138 | } 139 | 140 | Account? importAccountFromMnemonic(String phrase, int index) { 141 | if (!this.isValidMnemonic(phrase)) { 142 | throw 'Invalid mnemonic phrase: ${phrase}'; 143 | } 144 | final seed = bip39.mnemonicToSeed(phrase); 145 | final hdKey = bip32.BIP32.fromSeed(seed); 146 | final rootString = "m/44'/313'/0'/0/$index"; 147 | final childKey = hdKey.derivePath(rootString); 148 | String prvKeys = numbers.bytesToHex(childKey.privateKey!); 149 | var account = Account(prvKeys); 150 | return this.add(account); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /lib/src/contract/abi.dart: -------------------------------------------------------------------------------- 1 | abstract class ABIObject { 2 | List? events; 3 | List? fields; 4 | String? vname; 5 | List? params; 6 | List? transitions; 7 | } 8 | 9 | /* 10 | * ABI object is extracted from scilla contract code. 11 | * In Zilliqa's network, each contract code is deployed and executed through scilla-runner, 12 | * which runs along-side with the Full-Node. 13 | * For developer's side, we can use scilla-checker to check the code, parse for the ABI, 14 | * and use scilla-runner try make contract call. 15 | * You can run a scilla runner/checker from your local machine, or make api calls remotely to [https://scilla-runner.zilliqa.com] 16 | * In laksa, we use remote endpont call to get ABI, however you can set scillaProvider using your own local address. 17 | */ 18 | class ABI implements ABIObject { 19 | List? events; 20 | List? fields; 21 | String? vname; 22 | List? params; 23 | List? transitions; 24 | 25 | ABI(Map Abi) { 26 | // because ABI is , to get the type annotation ready, 27 | // we need to use List.from(),which will get the correct list. 28 | // then we can extract the List type to List 29 | 30 | this.events = List.from(Abi['events']); 31 | this.fields = List.from(Abi['fields']); 32 | this.vname = Abi['vname']; 33 | this.params = List.from(Abi['params']); 34 | this.transitions = List.from(Abi['transitions']); 35 | } 36 | 37 | String? getName() { 38 | return this.vname; 39 | } 40 | 41 | List? getInitParams() { 42 | return this.params; 43 | } 44 | 45 | List? getInitParamTypes() { 46 | if (this.params!.isNotEmpty) { 47 | return getParamTypes(this.params!); 48 | } else { 49 | return null; 50 | } 51 | } 52 | 53 | List>? getFields() { 54 | return this.fields as List>?; 55 | } 56 | 57 | List? getFieldsTypes() { 58 | if (this.fields!.isNotEmpty) { 59 | return getParamTypes(this.fields!); 60 | } else { 61 | return null; 62 | } 63 | } 64 | 65 | List? getTransitions() { 66 | return this.transitions; 67 | } 68 | 69 | List? getTransitionsParamTypes() { 70 | List returnArray = []; 71 | if (this.transitions!.isNotEmpty) { 72 | for (int i = 0; i < this.transitions!.length; i += 1) { 73 | returnArray[i] = getParamTypes(this.transitions![i]['params']); 74 | } 75 | } 76 | if (returnArray.isNotEmpty) { 77 | return returnArray as List?>?; 78 | } else { 79 | return null; 80 | } 81 | } 82 | 83 | List? getEvents() { 84 | return this.events; 85 | } 86 | } 87 | 88 | List? getParamTypes(List list) { 89 | List keyList2 = []; 90 | var boolList = list.map((obj) { 91 | keyList2.addAll(obj.keys as Iterable); 92 | return false; 93 | }); 94 | if (boolList.isNotEmpty) { 95 | return keyList2; 96 | } else { 97 | return null; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /lib/src/contract/api.dart: -------------------------------------------------------------------------------- 1 | abstract class BaseContract {} 2 | -------------------------------------------------------------------------------- /lib/src/contract/contract.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:convert'; 3 | import 'package:laksadart/laksadart.dart'; 4 | import 'package:laksadart/src/account/account.dart'; 5 | import 'package:laksadart/src/account/wallet.dart'; 6 | import 'package:laksadart/src/account/address.dart'; 7 | import 'package:laksadart/src/messenger/messenger.dart'; 8 | import 'package:laksadart/src/transaction/transaction.dart'; 9 | import 'util.dart'; 10 | import 'api.dart'; 11 | 12 | class Contract implements BaseContract { 13 | String code = ''; 14 | List? init = []; 15 | String? contractAddress; 16 | ContractStatus? status; 17 | Transaction? transaction; 18 | int? version; 19 | bool? toDS = false; 20 | //----- factory ---// 21 | Messenger? messenger; 22 | Wallet? wallet; 23 | Account? get signer => this.wallet!.getAccount(this.wallet!.defaultAccount); 24 | 25 | Contract( 26 | {required Map params, 27 | Messenger? messenger, 28 | Wallet? wallet, 29 | ContractStatus? status = ContractStatus.INITIALISED, 30 | bool? toDS = false}) { 31 | this.code = params['code'] ?? ''; 32 | this.init = params['init'] ?? []; 33 | this.version = params['version'] ?? 0; 34 | this.transaction = params['transaction'] ?? null; 35 | this.contractAddress = params['ContractAddress'] ?? ''; 36 | this.status = params['status'] ?? status; 37 | // factory 38 | this.messenger = messenger; 39 | this.wallet = wallet; 40 | this.toDS = toDS; 41 | } 42 | 43 | bool isInitialised() { 44 | return this.status == ContractStatus.INITIALISED; 45 | } 46 | 47 | bool isSigned() { 48 | return this.status == ContractStatus.SIGNED; 49 | } 50 | 51 | bool isSent() { 52 | return this.status == ContractStatus.SENT; 53 | } 54 | 55 | bool isDeployed() { 56 | return this.status == ContractStatus.DEPLOYED; 57 | } 58 | 59 | bool isRejected() { 60 | return this.status == ContractStatus.REJECTED; 61 | } 62 | 63 | Map get deployPayload => { 64 | 'version': this.version, 65 | 'amount': BigInt.from(0), 66 | 'toAddr': ZilAddress.toValidAddress('0x' + '0' * 40), 67 | 'code': this.code, 68 | 'data': json.encode(this.init).replaceAll(r"/\\", '"') 69 | }; 70 | 71 | Map get callPayload => { 72 | 'version': this.version, 73 | 'toAddr': ZilAddress.toCheckSum(this.contractAddress!) 74 | }; 75 | 76 | void setStatus(ContractStatus status) { 77 | this.status = status; 78 | } 79 | 80 | Future deploy( 81 | {int? gasLimit, 82 | BigInt? gasPrice, 83 | Account? account, 84 | String? passphrase, 85 | int maxAttempts = 20, 86 | int interval = 1000, 87 | bool toDS = false}) async { 88 | var defaultGasLimit = 2500000000; 89 | var defaultGasPrice = BigInt.from(100000000); 90 | 91 | if (this.init == null) { 92 | throw 'Cannot deploy without code or ABI.'; 93 | } 94 | 95 | try { 96 | this.setDeployPayload( 97 | gasLimit: gasLimit ?? defaultGasLimit, 98 | gasPrice: gasPrice ?? defaultGasPrice, 99 | toDS: toDS); 100 | await this.sendContract( 101 | account: account ?? this.signer, passphrase: passphrase); 102 | await this.confirmTx(maxAttempts: maxAttempts, interval: interval); 103 | return this; 104 | } catch (err) { 105 | rethrow; 106 | } 107 | } 108 | 109 | Future call( 110 | {String? transition, 111 | params, 112 | BigInt? amount, 113 | int gasLimit = 1000, 114 | BigInt? gasPrice, 115 | Account? account, 116 | String? passphrase, 117 | int maxAttempts = 20, 118 | int interval = 1000, 119 | bool toDS = false}) async { 120 | if (this.contractAddress == null) { 121 | throw 'Contract has not been deployed!'; 122 | } 123 | var defaultAmount = BigInt.from(10000); 124 | var defaultGasPrice = BigInt.from(100000000); 125 | try { 126 | this.setCallPayload( 127 | transition: transition, 128 | params: params, 129 | amount: amount ?? defaultAmount, 130 | gasLimit: gasLimit, 131 | gasPrice: gasPrice ?? defaultGasPrice, 132 | toDS: toDS); 133 | await this.sendContract( 134 | account: account ?? this.signer, passphrase: passphrase); 135 | await this.confirmTx(maxAttempts: maxAttempts, interval: interval); 136 | return this; 137 | } catch (err) { 138 | rethrow; 139 | } 140 | } 141 | 142 | Future confirmTx( 143 | {int maxAttempts = 20, int interval = 1000}) async { 144 | try { 145 | await this.transaction!.confirm( 146 | txHash: this.transaction!.transactionID, 147 | maxAttempts: maxAttempts, 148 | interval: interval); 149 | if (this.transaction!.receipt == null || 150 | !this.transaction!.receipt!['success']) { 151 | this.setStatus(ContractStatus.REJECTED); 152 | return this; 153 | } 154 | this.setStatus(ContractStatus.DEPLOYED); 155 | return this; 156 | } catch (error) { 157 | rethrow; 158 | } 159 | } 160 | 161 | Future sendContract({Account? account, String? passphrase}) async { 162 | try { 163 | await this.signTxn(account: account, passphrase: passphrase); 164 | 165 | var txnSent = await this.transaction!.sendTransaction(); 166 | 167 | var transaction = txnSent!.transaction; 168 | var result = txnSent.result; 169 | 170 | this.contractAddress = this.contractAddress!.isNotEmpty 171 | ? this.contractAddress 172 | : result['ContractAddress']; 173 | 174 | this.transaction = transaction.map((obj) { 175 | var resultMap = Map.from(obj); 176 | var newMap = {'TranID': result['TranID']}; 177 | resultMap.addAll(newMap); 178 | return resultMap; 179 | }); 180 | 181 | this.setStatus(ContractStatus.SENT); 182 | 183 | return this; 184 | } catch (error) { 185 | rethrow; 186 | } 187 | } 188 | 189 | Future signTxn({Account? account, String? passphrase}) async { 190 | Account signingAccount = account ?? this.signer!; 191 | try { 192 | this.transaction = await signingAccount.signTransaction(this.transaction, 193 | passphrase: passphrase); 194 | this.setStatus(ContractStatus.SIGNED); 195 | 196 | return this; 197 | } catch (error) { 198 | rethrow; 199 | } 200 | } 201 | 202 | Future getState() async { 203 | if (this.status != ContractStatus.DEPLOYED) { 204 | return null; 205 | } 206 | var response = await this 207 | .messenger! 208 | .send(RPCMethod.GetSmartContractState, this.contractAddress); 209 | return response.result; 210 | } 211 | 212 | Contract setInitParamsValues(List initParams, List arrayOfValues) { 213 | List result = setParamValues(initParams, arrayOfValues); 214 | this.init = result; 215 | return this; 216 | } 217 | 218 | Contract setDeployPayload( 219 | {BigInt? gasPrice, int? gasLimit, bool toDS = false}) { 220 | Map add = { 221 | 'gasPrice': gasPrice, 222 | 'gasLimit': gasLimit, 223 | }; 224 | Map params = Map.from(this.deployPayload); 225 | params.addAll(add); 226 | 227 | this.transaction = 228 | new Transaction(params: params, messenger: this.messenger, toDS: toDS); 229 | 230 | return this; 231 | } 232 | 233 | Contract setCallPayload( 234 | {String? transition, 235 | List? params, 236 | BigInt? amount, 237 | int? gasLimit, 238 | BigInt? gasPrice, 239 | bool toDS = false}) { 240 | Map msg = { 241 | '_tag': transition, 242 | 'params': params, 243 | }; 244 | Map txnParams = Map.from(this.callPayload); 245 | Map newMaps = { 246 | 'amount': amount, 247 | 'gasPrice': gasPrice, 248 | 'gasLimit': gasLimit, 249 | 'data': json.encode(msg) 250 | }; 251 | txnParams.addAll(newMaps); 252 | this.transaction = new Transaction( 253 | params: txnParams, messenger: this.messenger, toDS: toDS); 254 | return this; 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /lib/src/contract/factory.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:laksadart/src/core/zilliqa_module.dart'; 3 | import 'package:laksadart/src/messenger/messenger.dart'; 4 | import 'package:laksadart/src/account/wallet.dart'; 5 | import 'package:laksadart/src/transaction/index.dart'; 6 | import 'package:laksadart/src/crypto/index.dart'; 7 | import 'package:laksadart/src/utils/numbers.dart' as numbers; 8 | 9 | import 'util.dart'; 10 | import 'contract.dart'; 11 | import 'testScilla.dart'; 12 | 13 | class Contracts implements ZilliqaModule { 14 | Messenger? _messenger; 15 | Wallet? _wallet; 16 | 17 | @override 18 | Messenger get messenger => this._messenger!; 19 | 20 | @override 21 | void set messenger(Messenger? messenger) => this._messenger = messenger; 22 | 23 | Wallet get wallet => this._wallet!; 24 | void set wallet(Wallet? wallet) => this._wallet = wallet; 25 | 26 | Contracts({Messenger? messenger, Wallet? wallet}) { 27 | this.messenger = messenger; 28 | this.wallet = wallet; 29 | } 30 | 31 | Contract newContract({String? code, List? init, int? version, bool? toDS}) { 32 | return new Contract( 33 | params: {'code': code, 'init': init, 'version': version}, 34 | messenger: this.messenger, 35 | wallet: this.wallet, 36 | status: ContractStatus.INITIALISED, 37 | toDS: toDS); 38 | } 39 | 40 | Contract at(Contract contract) { 41 | return new Contract(params: { 42 | 'code': contract.code, 43 | 'init': contract.init, 44 | 'version': contract.version, 45 | 'ContractAddress': contract.contractAddress, 46 | 'status': contract.status, 47 | 'transaction': contract.transaction, 48 | }, messenger: this.messenger, wallet: this.wallet, toDS: contract.toDS); 49 | } 50 | 51 | String getAddressForContract(Transaction tx) { 52 | var nonce = tx.txParams['nonce'] ? tx.txParams['nonce'] - 1 : 0; 53 | var newSha = SHA256() 54 | .update(numbers.hexToBytes(tx.senderAddress)) 55 | .update( 56 | numbers.hexToBytes(numbers.numberToHexArray(nonce, 16).join(''))) 57 | .toString(); 58 | return newSha.substring(24); 59 | } 60 | 61 | Future testContract({String? code, List? init, int? version}) async { 62 | TestScilla contract = new TestScilla( 63 | params: {'code': code, 'init': init, 'version': version}, 64 | messenger: this.messenger, 65 | status: ContractStatus.INITIALISED); 66 | Map result = await contract 67 | // decode ABI from code first 68 | .decodeABI(code: code) 69 | // we set the init params to decoded ABI 70 | .then((decoded) => decoded.setInitParamsValues( 71 | decoded.abi!.getInitParams()!, 72 | init as List>?)) 73 | // we get the current block number from node, and set it to params 74 | .then((inited) => inited.setBlockNumber(null)) 75 | // but we have to give it a test 76 | .then((ready) => ready.testCall(2000)) 77 | // now we change the status to wait for sign 78 | .then((state) => state.status == ContractStatus.TESTED 79 | ? {'abi': state.abi, 'init': state.init, 'status': state.status} 80 | : { 81 | 'abi': state.abi, 82 | 'init': state.init, 83 | 'status': ContractStatus.ERROR, 84 | }); 85 | return result['status'] == ContractStatus.TESTED; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /lib/src/contract/index.dart: -------------------------------------------------------------------------------- 1 | library contract; 2 | 3 | export 'abi.dart'; 4 | export 'api.dart'; 5 | export 'contract.dart'; 6 | export 'factory.dart'; 7 | export 'testScilla.dart'; 8 | export 'util.dart'; 9 | -------------------------------------------------------------------------------- /lib/src/contract/testScilla.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:convert'; 3 | import 'package:laksadart/src/messenger/messenger.dart'; 4 | import 'package:laksadart/src/transaction/transaction.dart'; 5 | import 'package:laksadart/src/provider/Middleware.dart'; 6 | import 'package:laksadart/src/provider/net.dart'; 7 | import 'util.dart'; 8 | import 'abi.dart'; 9 | 10 | import 'contract.dart'; 11 | 12 | class TestScilla extends Contract { 13 | String code = ''; 14 | List? init; 15 | String? contractAddress; 16 | int? version; 17 | Messenger? messenger; 18 | ContractStatus? status; 19 | Transaction? transaction; 20 | List blockchain = []; 21 | ABI? abi; 22 | 23 | TestScilla( 24 | {required Map params, Messenger? messenger, ContractStatus? status}) 25 | : super(params: params, messenger: messenger, status: status) { 26 | this.code = params['code'] ?? ''; 27 | this.init = params['init'] ?? []; 28 | this.version = params['version'] ?? 0; 29 | this.contractAddress = params['ContractAddress'] ?? ''; 30 | this.status = status; 31 | this.messenger = messenger; 32 | this.transaction = null; 33 | } 34 | 35 | Future testCall(gasLimit) async { 36 | try { 37 | RPCMiddleWare res = 38 | await this.messenger!.sendServer(Endpoint.ScillaCall, { 39 | 'code': this.code, 40 | 'init': json.encode(this.init), 41 | 'blockchain': json.encode(this.blockchain), 42 | 'gaslimit': gasLimit.toString() 43 | }); 44 | print(res.message); 45 | if (res.result.toString() != 'error') { 46 | this.setStatus(ContractStatus.TESTED); 47 | } else { 48 | this.setStatus(ContractStatus.ERROR); 49 | } 50 | return this; 51 | } catch (error) { 52 | rethrow; 53 | } 54 | } 55 | 56 | Future getABI({String? code}) async { 57 | /// the endpoint for sendServer has been set to scillaProvider 58 | try { 59 | RPCMiddleWare res = await this 60 | .messenger! 61 | .sendServer(Endpoint.ScillaCheck, {'code': code}); 62 | if (res.result.toString() != 'error' && res.message != null) { 63 | var decoded = json.decode(res.message); 64 | return decoded['contract_info']; 65 | } else { 66 | throw res.message; 67 | } 68 | } catch (error) { 69 | rethrow; 70 | } 71 | } 72 | 73 | get testPayload => this.getTestPayload(); 74 | 75 | Map getTestPayload() { 76 | var payload = this.deployPayload; 77 | var newList = List.from(this.init!); 78 | newList.addAll(this.blockchain); 79 | var newMap = { 80 | 'code': this.code, 81 | 'data': json.encode(newList).replaceAll(new RegExp(r'\\'), '') 82 | }; 83 | payload.addAll(newMap); 84 | return payload; 85 | } 86 | 87 | Future decodeABI({String? code}) async { 88 | try { 89 | this.setCode(code); 90 | var abiObj = await this.getABI(code: code); 91 | this.setABI(abiObj); 92 | return this; 93 | } catch (error) { 94 | rethrow; 95 | } 96 | } 97 | 98 | Future setBlockNumber(int? number) async { 99 | try { 100 | if (number != null) { 101 | this.setBlockchain(number.toString()); 102 | this.setCreationBlock(number.toString()); 103 | return this; 104 | } else if (number == null) { 105 | var res = await this.messenger!.send(RPCMethod.GetLatestTxBlock); 106 | if (res.result != null) { 107 | this.setBlockchain(res.result!.toMap()!['header']['BlockNum']); 108 | this.setCreationBlock(res.result!.toMap()!['header']['BlockNum']); 109 | return this; 110 | } else { 111 | return this; 112 | } 113 | } else { 114 | return this; 115 | } 116 | } catch (error) { 117 | rethrow; 118 | } 119 | } 120 | 121 | TestScilla setABI(Map abi) { 122 | this.abi = new ABI(abi as Map); 123 | return this; 124 | } 125 | 126 | TestScilla setCode(String? code) { 127 | this.code = code ?? ''; 128 | return this; 129 | } 130 | 131 | TestScilla setInitParamsValues( 132 | List initParams, List? arrayOfValues) { 133 | print(arrayOfValues); 134 | this.init = setParamValues(initParams, arrayOfValues); 135 | return this; 136 | } 137 | 138 | TestScilla setCreationBlock(String blockNumber) { 139 | var result = setParamValues([ 140 | {'vname': '_creation_block', 'type': 'BNum'} 141 | ], [ 142 | { 143 | 'vname': '_creation_block', 144 | 'type': 'BNum', 145 | 'value': BigInt.from(int.parse(blockNumber)).toString() 146 | } 147 | ]); 148 | var block = result[0]; 149 | List newList = List.from(this.init!); 150 | newList.add(block); 151 | this.init = List.from(newList); 152 | return this; 153 | } 154 | 155 | TestScilla setBlockchain(String blockNumber) { 156 | var result = setParamValues([ 157 | {'vname': 'BLOCKNUMBER', 'type': 'BNum'} 158 | ], [ 159 | { 160 | 'vname': 'BLOCKNUMBER', 161 | 'type': 'BNum', 162 | 'value': BigInt.from(int.parse(blockNumber)).toString() 163 | } 164 | ]); 165 | var block = result[0]; 166 | List newList = List.from(this.blockchain); 167 | newList.add(block); 168 | this.blockchain = List.from(newList); 169 | return this; 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /lib/src/contract/util.dart: -------------------------------------------------------------------------------- 1 | enum ContractStatus { 2 | INITIALISED, 3 | TESTED, 4 | ERROR, 5 | SIGNED, 6 | SENT, 7 | REJECTED, 8 | DEPLOYED 9 | } 10 | 11 | List setParamValues(List rawParams, List? newValues) { 12 | List newParams = []; 13 | 14 | for (int i = 0; i < rawParams.length; i += 1) { 15 | var v = rawParams[i]; 16 | var found = newValues!.firstWhere((test) { 17 | return test['vname'] == v['name'] || test['vname'] == v['vname']; 18 | }); 19 | var newMap = {'value': found['value'], 'vname': v['name'] ?? v['vname']}; 20 | Map newObj = Map.from(v); 21 | newObj.addAll(newMap); 22 | if (newObj['name'] != null) { 23 | newObj.remove('name'); 24 | } 25 | newParams.add(newObj); 26 | } 27 | 28 | return newParams; 29 | } 30 | -------------------------------------------------------------------------------- /lib/src/core/index.dart: -------------------------------------------------------------------------------- 1 | library core; 2 | 3 | export 'zilliqa_module.dart'; 4 | -------------------------------------------------------------------------------- /lib/src/core/zilliqa_module.dart: -------------------------------------------------------------------------------- 1 | import 'package:laksadart/laksadart.dart'; 2 | 3 | abstract class ZilliqaModule { 4 | Messenger get messenger; 5 | void set messenger(Messenger messenger); 6 | } 7 | 8 | abstract class AccountState { 9 | bool get isEncrypted; 10 | bool? isFound; 11 | static void setEncrypt() {} 12 | static void setFound() {} 13 | } 14 | -------------------------------------------------------------------------------- /lib/src/crypto/bech32.dart: -------------------------------------------------------------------------------- 1 | import 'package:bech32/bech32.dart'; 2 | import 'package:laksadart/src/utils/validators.dart' show isAddress; 3 | import 'package:laksadart/src/utils/numbers.dart' as numbers; 4 | import './checksum.dart'; 5 | 6 | final String HRP = 'zil'; 7 | 8 | List _convertBits(List data, int from, int to, {bool pad: true}) { 9 | var acc = 0; 10 | var bits = 0; 11 | List result = []; 12 | var maxv = (1 << to) - 1; 13 | 14 | data.forEach((v) { 15 | if (v < 0 || (v >> from) != 0) { 16 | throw Exception(); 17 | } 18 | acc = (acc << from) | v; 19 | bits += from; 20 | while (bits >= to) { 21 | bits -= to; 22 | result.add((acc >> bits) & maxv); 23 | } 24 | }); 25 | 26 | if (pad) { 27 | if (bits > 0) { 28 | result.add((acc << (to - bits)) & maxv); 29 | } 30 | } else if (bits >= from) { 31 | throw InvalidPadding("illegal zero padding"); 32 | } else if (((acc << (to - bits)) & maxv) != 0) { 33 | throw InvalidPadding("non zero"); 34 | } 35 | 36 | return result; 37 | } 38 | 39 | String toBech32Address(String address) { 40 | if (!isAddress(address)) { 41 | throw 'Invalid address format.'; 42 | } 43 | List addrBz = _convertBits( 44 | numbers.hexToBytes(address.replaceAll('0x', '')), 45 | 8, 46 | 5, 47 | ); 48 | 49 | var b32Class = Bech32(HRP, addrBz); 50 | return bech32.encode(b32Class); 51 | } 52 | 53 | String fromBech32Address(String address) { 54 | try { 55 | Bech32 res = bech32.decode(address); 56 | var hrp = res.hrp; 57 | var data = res.data; 58 | 59 | if (hrp != HRP) { 60 | throw InvalidHrp(); 61 | } 62 | 63 | List buf = _convertBits(data, 5, 8, pad: false); 64 | return toChecksum(numbers.bytesToHex(buf)); 65 | } catch (e) { 66 | rethrow; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /lib/src/crypto/checksum.dart: -------------------------------------------------------------------------------- 1 | import 'package:crypto/crypto.dart'; 2 | import 'package:laksadart/src/utils/numbers.dart' as numbers; 3 | 4 | String toChecksum(String address) { 5 | String stripAddress = numbers.strip0x(address.toLowerCase()); 6 | 7 | final hash = sha256.convert(numbers.hexToBytes(stripAddress)).toString(); 8 | String ret = '0x'; 9 | 10 | BigInt v = numbers.hexToInt(hash); 11 | 12 | for (int i = 0; i < stripAddress.length; i++) { 13 | if ('0123456789'.contains(stripAddress[i])) { 14 | ret += stripAddress[i]; 15 | } else { 16 | var checker = v & BigInt.from(2).pow(BigInt.from(255 - 6 * i).toInt()); 17 | ret += checker >= BigInt.from(1) 18 | ? stripAddress[i].toUpperCase() 19 | : stripAddress[i].toLowerCase(); 20 | } 21 | } 22 | 23 | return ret; 24 | } 25 | -------------------------------------------------------------------------------- /lib/src/crypto/dartRandom.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | import 'dart:typed_data'; 3 | 4 | import 'package:pointycastle/api.dart'; 5 | 6 | import '../utils/numbers.dart' as numbers; 7 | 8 | /// Utility to use dart:math's Random class to generate numbers used by 9 | /// pointycastle. 10 | class DartRandom implements SecureRandom { 11 | Random dartRandom; 12 | 13 | DartRandom(this.dartRandom); 14 | 15 | @override 16 | String get algorithmName => "DartRandom"; 17 | 18 | @override 19 | BigInt nextBigInteger(int bitLength) { 20 | int fullBytes = bitLength ~/ 8; 21 | BigInt main = numbers.bytesToInt(nextBytes(fullBytes)); 22 | 23 | /// forcing remainingBits to be calculate with bitLength 24 | int remainingBits = (bitLength - main.bitLength); 25 | int additional = remainingBits < 4 26 | ? dartRandom.nextInt(pow(2, remainingBits) as int) 27 | : remainingBits; 28 | BigInt additionalBit = (new BigInt.from(additional) << (fullBytes * 8)); 29 | BigInt result = main + additionalBit; 30 | return result; 31 | } 32 | 33 | @override 34 | Uint8List nextBytes(int count) { 35 | Uint8List list = new Uint8List(count); 36 | 37 | for (int i = 0; i < list.length; i++) { 38 | list[i] = nextUint8(); 39 | } 40 | return list; 41 | } 42 | 43 | @override 44 | int nextUint16() => dartRandom.nextInt(pow(2, 32) as int); 45 | 46 | @override 47 | int nextUint32() => dartRandom.nextInt(pow(2, 32) as int); 48 | 49 | @override 50 | int nextUint8() => dartRandom.nextInt(pow(2, 8) as int); 51 | 52 | @override 53 | void seed(CipherParameters params) { 54 | /// ignore, dartRandom will already be seeded if wanted 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lib/src/crypto/hmac-drbg.dart: -------------------------------------------------------------------------------- 1 | import 'package:crypto/crypto.dart'; 2 | import '../utils/common.dart'; 3 | 4 | class HMAC extends Hmac { 5 | late Hash hash; 6 | 7 | late dynamic key; 8 | late List inner; 9 | int get blockSize => hash.blockSize ~/ 8; 10 | 11 | HMAC(hash, key) : super(hash, key) { 12 | this.hash = hash; 13 | this.key = key; 14 | this.inner = List.from([]); 15 | } 16 | 17 | HMAC hmac() { 18 | return new HMAC(this.hash, this.key); 19 | } 20 | 21 | HMAC update(List data) { 22 | this.inner.insertAll(this.inner.length, data); 23 | return this; 24 | } 25 | 26 | Digest digest() { 27 | return this.convert(this.inner); 28 | } 29 | } 30 | 31 | class DRBG { 32 | Hash? hash; 33 | late List entropy; 34 | late List nonce; 35 | late List pers; 36 | int get outLen => hash!.blockSize * 4; 37 | late dynamic K; 38 | late dynamic V; 39 | dynamic _reseed; 40 | dynamic reseedInterval; 41 | 42 | DRBG( 43 | {Hash? hash, 44 | dynamic entropy, 45 | dynamic nonce, 46 | dynamic pers, 47 | String? entropyEnc, 48 | String? nonceEnc, 49 | String? persEnc}) { 50 | this.hash = hash; 51 | this.entropy = (entropy is List) 52 | ? entropy 53 | : toArray(entropy, entropyEnc is String ? entropyEnc : 'hex'); 54 | this.nonce = (nonce is List) 55 | ? nonce 56 | : toArray(nonce, nonceEnc is String ? nonceEnc : 'hex'); 57 | this.pers = (pers is List) 58 | ? pers 59 | : toArray(pers, persEnc is String ? persEnc : 'hex'); 60 | this.init(); 61 | } 62 | 63 | void init() { 64 | List seed = List.filled( 65 | this.entropy.length + this.nonce.length + this.pers.length, 0); 66 | seed.setRange(0, this.entropy.length, this.entropy); 67 | seed.setRange(this.entropy.length, this.entropy.length + this.nonce.length, 68 | this.nonce); 69 | seed.setRange( 70 | this.entropy.length + this.nonce.length, seed.length, this.pers); 71 | 72 | this.K = []..length = this.outLen ~/ 8; 73 | this.V = []..length = this.outLen ~/ 8; 74 | for (int i = 0; i < this.V.length; i++) { 75 | this.K[i] = 0x00; 76 | this.V[i] = 0x01; 77 | } 78 | this._update(seed); 79 | this._reseed = 1; 80 | this.reseedInterval = 0x1000000000000; 81 | 82 | /// 2^48 83 | } 84 | 85 | HMAC _hmac() { 86 | return new HMAC(this.hash, this.K.cast()); 87 | } 88 | 89 | void _update(seed) { 90 | var kmac = this._hmac().update(this.V.cast()).update([0x00]); 91 | if (seed != null) { 92 | kmac.update(seed); 93 | } 94 | this.K = kmac.digest().bytes; 95 | this.V = this._hmac().update(this.V.cast()).digest().bytes; 96 | if (seed == null) return; 97 | this.K = this 98 | ._hmac() 99 | .update(this.V.cast()) 100 | .update([0x01]) 101 | .update(seed) 102 | .digest() 103 | .bytes; 104 | this.V = this._hmac().update(this.V.cast()).digest().bytes; 105 | } 106 | 107 | String generate(int len, [dynamic add]) { 108 | var temp = []; 109 | 110 | while (temp.length < len) { 111 | this.V = this._hmac().update(this.V).digest().bytes; 112 | temp.insertAll(temp.length, this.V); 113 | } 114 | 115 | var res = temp.sublist(0, len); 116 | this._update(add); 117 | this._reseed++; 118 | return toHex(res); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /lib/src/crypto/index.dart: -------------------------------------------------------------------------------- 1 | library crypto; 2 | 3 | export 'keystore/key_store.dart'; 4 | export 'dartRandom.dart'; 5 | export 'hmac-drbg.dart'; 6 | export 'schnorr.dart'; 7 | export 'signature.dart'; 8 | export 'sha256digest.dart'; 9 | export 'isolates.dart'; 10 | export 'checksum.dart'; 11 | -------------------------------------------------------------------------------- /lib/src/crypto/isolates.dart: -------------------------------------------------------------------------------- 1 | import 'dart:isolate'; 2 | import 'dart:async'; 3 | import 'schnorr.dart'; 4 | import 'keystore/api.dart'; 5 | 6 | Future asyncEncrypt(String? prvKey, String? psw, 7 | [Map? options]) async { 8 | final response = new ReceivePort(); 9 | 10 | await Isolate.spawn( 11 | _isolate_encrypt, 12 | response.sendPort, 13 | onExit: response.sendPort, 14 | ); 15 | 16 | final sendPort = await response.first as SendPort; 17 | final receivePort = new ReceivePort(); 18 | 19 | sendPort.send([prvKey, psw, options, receivePort.sendPort]); 20 | 21 | try { 22 | final result = await receivePort.first; 23 | var resultString = result.toString(); 24 | 25 | if (resultString.startsWith('@@LaksaError@@')) { 26 | throw resultString.substring(14); 27 | } 28 | response.close(); 29 | return result; 30 | } catch (e) { 31 | rethrow; 32 | } 33 | } 34 | 35 | void _isolate_encrypt(SendPort initialReplyTo) { 36 | final port = new ReceivePort(); 37 | 38 | initialReplyTo.send(port.sendPort); 39 | 40 | port.listen((message) async { 41 | try { 42 | final prvKey = message[0]; 43 | final psw = message[1]; 44 | final options = message[2]; 45 | final send = message.last; 46 | var encrypted; 47 | encrypted = await encrypt(prvKey, psw, options); 48 | 49 | send.send(encrypted); 50 | } catch (e) { 51 | message.last.send('@@LaksaError@@$e'); 52 | } 53 | }); 54 | } 55 | 56 | Future asyncDecrypt( 57 | Map? keyStore, 58 | String? psw, 59 | ) async { 60 | final response = new ReceivePort(); 61 | 62 | await Isolate.spawn( 63 | _isolate_decrypt, 64 | response.sendPort, 65 | onExit: response.sendPort, 66 | onError: response.sendPort, 67 | ); 68 | 69 | final sendPort = await response.first as SendPort; 70 | final receivePort = new ReceivePort(); 71 | 72 | sendPort.send([keyStore, psw, receivePort.sendPort]); 73 | 74 | try { 75 | final result = await receivePort.first; 76 | var resultString = result.toString(); 77 | 78 | if (resultString.startsWith('@@LaksaError@@')) { 79 | throw resultString.substring(14); 80 | } 81 | response.close(); 82 | return result; 83 | } catch (e) { 84 | rethrow; 85 | } 86 | } 87 | 88 | void _isolate_decrypt(SendPort initialReplyTo) { 89 | final port = new ReceivePort(); 90 | 91 | initialReplyTo.send(port.sendPort); 92 | 93 | port.listen((message) async { 94 | try { 95 | final send = message.last as SendPort; 96 | final keyStore = message[0] as Map; 97 | final psw = message[1] as String; 98 | 99 | var decrypted = await decrypt(keyStore, psw); 100 | send.send(decrypted); 101 | } catch (e) { 102 | message.last.send('@@LaksaError@@$e'); 103 | } 104 | }); 105 | } 106 | 107 | Future asyncGeneratePrivateKey() async { 108 | final response = new ReceivePort(); 109 | 110 | await Isolate.spawn(_isolate_genratePrvKey, response.sendPort, 111 | onExit: response.sendPort); 112 | 113 | final sendPort = await response.first as SendPort; 114 | 115 | final receivePort = new ReceivePort(); 116 | 117 | sendPort.send([receivePort.sendPort]); 118 | 119 | try { 120 | final result = await receivePort.first; 121 | var resultString = result.toString(); 122 | 123 | if (resultString.startsWith('@@LaksaError@@')) { 124 | throw resultString.substring(14); 125 | } 126 | response.close(); 127 | return result; 128 | } catch (e) { 129 | rethrow; 130 | } 131 | } 132 | 133 | void _isolate_genratePrvKey(SendPort initialReplyTo) { 134 | final port = new ReceivePort(); 135 | 136 | initialReplyTo.send(port.sendPort); 137 | 138 | port.listen((message) { 139 | try { 140 | final send = message.last as SendPort; 141 | 142 | send.send(generatePrivateKey()); 143 | } catch (e) { 144 | message.last.send('@@LaksaError@@$e'); 145 | } 146 | }); 147 | } 148 | 149 | Future asyncSchnorrSign( 150 | String? privateKey, 151 | Map txnDetails, 152 | ) async { 153 | final response = new ReceivePort(); 154 | 155 | await Isolate.spawn( 156 | _isolate_SchnorrSign, 157 | response.sendPort, 158 | onExit: response.sendPort, 159 | ); 160 | 161 | final sendPort = await response.first as SendPort; 162 | 163 | final receivePort = new ReceivePort(); 164 | 165 | sendPort.send([privateKey, txnDetails, receivePort.sendPort]); 166 | 167 | try { 168 | final result = await receivePort.first; 169 | var resultString = result.toString(); 170 | 171 | if (resultString.startsWith('@@LaksaError@@')) { 172 | throw resultString.substring(14); 173 | } 174 | response.close(); 175 | return result; 176 | } catch (e) { 177 | rethrow; 178 | } 179 | } 180 | 181 | void _isolate_SchnorrSign(SendPort initialReplyTo) { 182 | final port = new ReceivePort(); 183 | 184 | initialReplyTo.send(port.sendPort); 185 | 186 | port.listen((message) { 187 | try { 188 | final prv = message[0] as String; 189 | final txn = message[1] as Map; 190 | final send = message.last as SendPort; 191 | send.send(SchnorrSign(prv, txn)); 192 | } catch (e) { 193 | message.last.send('@@LaksaError@@$e'); 194 | } 195 | }); 196 | } 197 | 198 | Future asyncGetPubKeyFromPrivateKey( 199 | String privateKey, 200 | ) async { 201 | final response = new ReceivePort(); 202 | 203 | await Isolate.spawn( 204 | _isolate_getPubKeyFromPrivateKey, 205 | response.sendPort, 206 | ); 207 | 208 | final sendPort = await response.first as SendPort; 209 | 210 | final receivePort = new ReceivePort(); 211 | 212 | sendPort.send([privateKey, receivePort.sendPort]); 213 | 214 | try { 215 | final result = await receivePort.first; 216 | var resultString = result.toString(); 217 | 218 | if (resultString.startsWith('@@LaksaError@@')) { 219 | throw resultString.substring(14); 220 | } 221 | response.close(); 222 | return result; 223 | } catch (e) { 224 | rethrow; 225 | } 226 | } 227 | 228 | void _isolate_getPubKeyFromPrivateKey(SendPort initialReplyTo) { 229 | final port = new ReceivePort(); 230 | 231 | initialReplyTo.send(port.sendPort); 232 | 233 | port.listen((message) { 234 | try { 235 | final prv = message[0] as String; 236 | final send = message.last as SendPort; 237 | 238 | send.send(getPubKeyFromPrivateKey(prv)); 239 | } catch (e) { 240 | message.last.send('@@LaksaError@@$e'); 241 | } 242 | }); 243 | } 244 | 245 | Future asyncGetAddressFromPrivateKey( 246 | String privateKey, 247 | ) async { 248 | final response = new ReceivePort(); 249 | 250 | await Isolate.spawn( 251 | _isolate_getAddressFromPrivateKey, 252 | response.sendPort, 253 | ); 254 | 255 | final sendPort = await response.first as SendPort; 256 | 257 | final receivePort = new ReceivePort(); 258 | 259 | sendPort.send([privateKey, receivePort.sendPort]); 260 | 261 | try { 262 | final result = await receivePort.first; 263 | var resultString = result.toString(); 264 | 265 | if (resultString.startsWith('@@LaksaError@@')) { 266 | throw resultString.substring(14); 267 | } 268 | response.close(); 269 | return result; 270 | } catch (e) { 271 | rethrow; 272 | } 273 | } 274 | 275 | void _isolate_getAddressFromPrivateKey(SendPort initialReplyTo) { 276 | final port = new ReceivePort(); 277 | 278 | initialReplyTo.send(port.sendPort); 279 | 280 | port.listen((message) { 281 | try { 282 | final prv = message[0] as String; 283 | final send = message.last as SendPort; 284 | 285 | send.send(getAddressFromPrivateKey(prv)); 286 | } catch (e) { 287 | message.last.send('@@LaksaError@@$e'); 288 | } 289 | }); 290 | } 291 | 292 | Future asyncGetAddressFromPublicKey( 293 | String pubKey, 294 | ) async { 295 | final response = new ReceivePort(); 296 | 297 | await Isolate.spawn( 298 | _isolate_getAddressFromPublicKey, 299 | response.sendPort, 300 | ); 301 | 302 | final sendPort = await response.first as SendPort; 303 | 304 | final receivePort = new ReceivePort(); 305 | 306 | sendPort.send([pubKey, receivePort.sendPort]); 307 | 308 | try { 309 | final result = await receivePort.first; 310 | var resultString = result.toString(); 311 | 312 | if (resultString.startsWith('@@LaksaError@@')) { 313 | throw resultString.substring(14); 314 | } 315 | response.close(); 316 | return result; 317 | } catch (e) { 318 | rethrow; 319 | } 320 | } 321 | 322 | void _isolate_getAddressFromPublicKey(SendPort initialReplyTo) { 323 | final port = new ReceivePort(); 324 | 325 | initialReplyTo.send(port.sendPort); 326 | 327 | port.listen((message) { 328 | try { 329 | final pub = message[0] as String; 330 | final send = message.last as SendPort; 331 | 332 | var signed = getAddressFromPublicKey(pub); 333 | send.send(signed); 334 | } catch (e) { 335 | message.last.send('@@LaksaError@@$e'); 336 | } 337 | }); 338 | } 339 | 340 | class IsolateFunction { 341 | List? params; 342 | Function? func; 343 | 344 | final response = new ReceivePort(); 345 | final receivePort = new ReceivePort(); 346 | Isolate? _isolate_instance; 347 | 348 | IsolateFunction({this.params, this.func}); 349 | 350 | run() async { 351 | _isolate_instance = await Isolate.spawn( 352 | _isolate_function, response.sendPort, 353 | onExit: response.sendPort); 354 | final sendPort = await response.first as SendPort; 355 | 356 | sendPort.send([ 357 | this.func, 358 | receivePort.sendPort, 359 | this.params!.iterator, 360 | ]); 361 | try { 362 | final result = await receivePort.first; 363 | var resultString = result.toString(); 364 | 365 | if (resultString.startsWith('@@LaksaError@@')) { 366 | throw resultString.substring(14); 367 | } 368 | 369 | kill(); 370 | return result; 371 | } catch (e) { 372 | rethrow; 373 | } 374 | } 375 | 376 | void kill() { 377 | if (_isolate_instance != null) { 378 | _isolate_instance!.kill(); 379 | response.close(); 380 | receivePort.close(); 381 | } 382 | } 383 | } 384 | 385 | void _isolate_function(SendPort initialReplyTo) { 386 | final port = new ReceivePort(); 387 | 388 | initialReplyTo.send(port.sendPort); 389 | 390 | port.listen((message) async { 391 | final func = message[0]; 392 | final send = message[1] as SendPort; 393 | try { 394 | final sendParams = message.removeRange(0, 2); 395 | 396 | var result = await func(sendParams); 397 | send.send(result); 398 | } catch (e) { 399 | send.send('@@LaksaError@@$e'); 400 | } 401 | }); 402 | } 403 | -------------------------------------------------------------------------------- /lib/src/crypto/keystore/api.dart: -------------------------------------------------------------------------------- 1 | export 'key_store.dart'; 2 | -------------------------------------------------------------------------------- /lib/src/crypto/keystore/function.dart: -------------------------------------------------------------------------------- 1 | part of 'key_store.dart'; 2 | 3 | final String ALGO_IDENTIFIER = 'aes-128-ctr'; 4 | 5 | Future encrypt(String privateKey, String passphrase, 6 | [Map? options]) async { 7 | Uint8List uuid = new Uint8List(16); 8 | 9 | String salt = crypto.randomHex(64); 10 | List iv = crypto.randomBytes(16); 11 | String? kdf = 'scrypt'; 12 | int? level = 8192; 13 | int? n = kdf == 'pbkdf2' ? 262144 : level; 14 | if (options == null) { 15 | kdf = 'scrypt'; 16 | level = 8192; 17 | n = kdf == 'pbkdf2' ? 262144 : level; 18 | } else { 19 | kdf = options['kdf'] is String ? options['kdf'] : 'scrypt'; 20 | level = options['level'] is int ? options['level'] : 8192; 21 | n = kdf == 'pbkdf2' ? 262144 : level!; 22 | } 23 | 24 | Map kdfParams = { 25 | 'salt': salt, 26 | 'n': n, 27 | 'r': 8, 28 | 'p': 1, 29 | 'dklen': 32 30 | }; 31 | 32 | List encodedPassword = utf8.encode(passphrase); 33 | _KeyDerivator derivator = getDerivedKey(kdf, kdfParams); 34 | List derivedKey = derivator.deriveKey(encodedPassword); 35 | 36 | List ciphertextBytes = await _encryptPrivateKey( 37 | derivator, encodedPassword as Uint8List, iv as Uint8List, privateKey); 38 | 39 | List macBuffer = derivedKey.sublist(16, 32) + 40 | ciphertextBytes + 41 | iv + 42 | ALGO_IDENTIFIER.codeUnits; 43 | 44 | String mac = numbers.bytesToHex( 45 | new HMAC(sha256, derivedKey).update(macBuffer).digest().bytes); 46 | 47 | Map map = { 48 | 'crypto': { 49 | 'cipher': 'aes-128-ctr', 50 | 'cipherparams': {'iv': numbers.bytesToHex(iv)}, 51 | 'ciphertext': numbers.bytesToHex(ciphertextBytes), 52 | 'kdf': kdf, 53 | 'kdfparams': json.encode(kdfParams), 54 | 'mac': mac, 55 | }, 56 | 'id': Uuid.unparse(uuid), 57 | 'version': 3, 58 | }; 59 | String result = json.encode(map); 60 | return result; 61 | } 62 | 63 | Future decrypt(Map keyStore, String passphrase) async { 64 | List ciphertext = numbers.hexToBytes(keyStore['crypto']['ciphertext']); 65 | String? kdf = keyStore['crypto']['kdf']; 66 | 67 | Map kdfparams = keyStore['crypto']['kdfparams'] is String 68 | ? json.decode(keyStore['crypto']['kdfparams']) 69 | : keyStore['crypto']['kdfparams']; 70 | var cipherparams = keyStore['crypto']["cipherparams"]; 71 | var iv = numbers.hexToBytes(cipherparams["iv"]); 72 | 73 | List encodedPassword = utf8.encode(passphrase); 74 | _KeyDerivator derivator = getDerivedKey(kdf, kdfparams); 75 | List derivedKey = derivator.deriveKey(encodedPassword); 76 | List macBuffer = 77 | derivedKey.sublist(16, 32) + ciphertext + iv + ALGO_IDENTIFIER.codeUnits; 78 | 79 | String mac = numbers.bytesToHex( 80 | new HMAC(sha256, derivedKey).update(macBuffer).digest().bytes); 81 | 82 | String macString = keyStore['crypto']['mac']; 83 | 84 | Function eq = const ListEquality().equals; 85 | if (!eq(mac.toUpperCase().codeUnits, macString.toUpperCase().codeUnits)) { 86 | throw 'Decryption Failed'; 87 | } 88 | 89 | var aesKey = derivedKey.sublist(0, 16); 90 | var encryptedPrivateKey = 91 | numbers.hexToBytes(keyStore["crypto"]["ciphertext"]); 92 | 93 | var aes = _initCipher(false, aesKey, iv); 94 | 95 | var privateKeyByte = await aes.process(encryptedPrivateKey as Uint8List); 96 | return numbers.bytesToHex(privateKeyByte); 97 | } 98 | -------------------------------------------------------------------------------- /lib/src/crypto/keystore/key_derivator.dart: -------------------------------------------------------------------------------- 1 | part of 'key_store.dart'; 2 | 3 | abstract class _KeyDerivator { 4 | Uint8List deriveKey(List password); 5 | 6 | String getName(); 7 | Map encode(); 8 | } 9 | 10 | class _PBDKDF2KeyDerivator extends _KeyDerivator { 11 | final int? iterations; 12 | final Uint8List salt; 13 | final int? dklen; 14 | 15 | /// The docs (https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition) 16 | /// say that HMAC with SHA-256 is the only mac supported at the moment 17 | static final Mac mac = new HMac(new SHA256Digest(), 64); 18 | 19 | _PBDKDF2KeyDerivator(this.iterations, this.salt, this.dklen); 20 | 21 | @override 22 | Uint8List deriveKey(List password) { 23 | var impl = new pbkdf2.PBKDF2KeyDerivator(mac) 24 | ..init(new Pbkdf2Parameters(salt, iterations!, dklen!)); 25 | 26 | return impl.process(password as Uint8List); 27 | } 28 | 29 | @override 30 | Map encode() { 31 | return { 32 | 'c': iterations, 33 | 'dklen': dklen, 34 | 'prf': 'hmac-sha256', 35 | 'salt': numbers.bytesToHex(salt) 36 | }; 37 | } 38 | 39 | @override 40 | String getName() { 41 | return "pbkdf2"; 42 | } 43 | } 44 | 45 | class _ScryptKeyDerivator extends _KeyDerivator { 46 | final int? dklen; 47 | final int? n; 48 | final int? r; 49 | final int? p; 50 | final List salt; 51 | 52 | _ScryptKeyDerivator(this.dklen, this.n, this.r, this.p, this.salt); 53 | 54 | @override 55 | Uint8List deriveKey(List password) { 56 | var impl = new scrypt.Scrypt() 57 | ..init(new ScryptParameters(n!, r!, p!, dklen!, salt as Uint8List)); 58 | 59 | return impl.process(password as Uint8List); 60 | } 61 | 62 | @override 63 | Map encode() { 64 | return { 65 | "dklen": dklen, 66 | "n": n, 67 | "r": r, 68 | "p": p, 69 | "salt": numbers.bytesToHex(salt), 70 | }; 71 | } 72 | 73 | @override 74 | String getName() => "scrypt"; 75 | } 76 | -------------------------------------------------------------------------------- /lib/src/crypto/keystore/key_store.dart: -------------------------------------------------------------------------------- 1 | library keyStore; 2 | 3 | import 'dart:core'; 4 | import 'dart:convert'; 5 | import 'dart:typed_data'; 6 | import 'dart:async'; 7 | import 'package:crypto/crypto.dart'; 8 | import 'package:collection/collection.dart'; 9 | import 'package:uuid/uuid.dart'; 10 | import 'package:pointycastle/api.dart'; 11 | import 'package:pointycastle/digests/sha256.dart'; 12 | import 'package:pointycastle/key_derivators/api.dart'; 13 | import 'package:pointycastle/key_derivators/pbkdf2.dart' as pbkdf2; 14 | import 'package:pointycastle/key_derivators/scrypt.dart' as scrypt; 15 | import 'package:pointycastle/macs/hmac.dart'; 16 | import 'package:pointycastle/block/aes_fast.dart'; 17 | import 'package:pointycastle/stream/ctr.dart'; 18 | import 'package:laksadart/src/utils/numbers.dart' as numbers; 19 | import '../hmac-drbg.dart'; 20 | import '../schnorr.dart' as crypto; 21 | 22 | part 'key_derivator.dart'; 23 | part 'function.dart'; 24 | part 'util.dart'; 25 | 26 | abstract class KeyStore { 27 | Map? get keyStoreMap; 28 | } 29 | -------------------------------------------------------------------------------- /lib/src/crypto/keystore/util.dart: -------------------------------------------------------------------------------- 1 | part of 'key_store.dart'; 2 | 3 | /// getDerivedKey by ``kdf`` type 4 | _KeyDerivator getDerivedKey(String? kdf, Map params) { 5 | var salt = numbers.hexToBytes(params['salt']); 6 | if (kdf == 'pbkdf2') { 7 | var c = params['c']; 8 | var dklen = params['dklen']; 9 | return new _PBDKDF2KeyDerivator(c, salt as Uint8List, dklen); 10 | } else if (kdf == 'scrypt') { 11 | var n = params['n']; 12 | var r = params['r']; 13 | var p = params['p']; 14 | var dklen = params['dklen']; 15 | return new _ScryptKeyDerivator(dklen, n, r, p, salt); 16 | } else { 17 | throw new ArgumentError('Only pbkdf2 and scrypt are supported'); 18 | } 19 | } 20 | 21 | CTRStreamCipher _initCipher(bool forEncryption, List key, List iv) { 22 | return new CTRStreamCipher(new AESFastEngine()) 23 | ..init( 24 | false, 25 | new ParametersWithIV( 26 | new KeyParameter(key as Uint8List), iv as Uint8List)); 27 | } 28 | 29 | List _encryptPrivateKey(_KeyDerivator _derivator, Uint8List _password, 30 | Uint8List _iv, String privateKey) { 31 | var derived = _derivator.deriveKey(_password); 32 | var aesKey = derived.sublist(0, 16); 33 | var aes = _initCipher(true, aesKey, _iv); 34 | return aes.process(numbers.hexToBytes(privateKey) as Uint8List); 35 | } 36 | -------------------------------------------------------------------------------- /lib/src/crypto/schnorr.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | import 'dart:typed_data'; 3 | import 'package:crypto/crypto.dart'; 4 | 5 | import 'package:pointycastle/api.dart'; 6 | import 'package:pointycastle/key_generators/api.dart'; 7 | import "package:pointycastle/key_generators/ec_key_generator.dart"; 8 | import "package:pointycastle/ecc/api.dart"; 9 | import "package:pointycastle/ecc/curves/secp256k1.dart"; 10 | 11 | import 'package:laksadart/src/utils/numbers.dart' as numbers; 12 | import 'package:laksadart/src/utils/transaction.dart'; 13 | import 'package:laksadart/src/utils/validators.dart' show isSignature; 14 | import 'hmac-drbg.dart'; 15 | import 'dartRandom.dart'; 16 | import 'signature.dart'; 17 | 18 | final PUBKEY_COMPRESSED_SIZE_BYTES = 33; 19 | 20 | final ECDomainParameters params = new ECCurve_secp256k1(); 21 | 22 | final ALG = 'Schnorr+SHA256 '.codeUnits; 23 | final ALG_LEN = 16; 24 | // The length in bytes of entropy inputs to HMAC-DRBG 25 | final ENT_LEN = 32; 26 | 27 | /// Generates a new private key using the random instance provided. Please make 28 | /// sure you're using a cryptographically secure generator. 29 | BigInt? generateNewPrivateKey(Random random) { 30 | var generator = new ECKeyGenerator(); 31 | 32 | var keyParams = new ECKeyGeneratorParameters(params); 33 | 34 | generator.init(new ParametersWithRandom(keyParams, new DartRandom(random))); 35 | 36 | AsymmetricKeyPair key = generator.generateKeyPair(); 37 | 38 | ECPrivateKey privateKey = key.privateKey as ECPrivateKey; 39 | return privateKey.d; 40 | } 41 | 42 | /// Generates a public key for the given private key using the ecdsa curve which 43 | /// Ethereum uses. 44 | String privateKeyToPublic(Uint8List privateKey) { 45 | BigInt privateKeyNum = numbers.bytesToInt(privateKey); 46 | 47 | ECPoint p = (params.G * privateKeyNum)!; 48 | Uint8List result = p.getEncoded(true); 49 | 50 | return numbers.bytesToHex(result); 51 | } 52 | 53 | String getPublic(BigInt private) { 54 | List privateKeyBytes = numbers.numberToBytes(private); 55 | return privateKeyToPublic(privateKeyBytes as Uint8List); 56 | } 57 | 58 | // privateKey generation 59 | String generatePrivateKey() { 60 | Random rng = new Random.secure(); 61 | BigInt prvKeyBigInt = generateNewPrivateKey(rng)!; 62 | return prvKeyBigInt.toRadixString(16); 63 | } 64 | 65 | /// publicKey calculation for Zilliqa 66 | String getPubKeyFromPrivateKey(String privateKey) { 67 | BigInt privateKeyBigInt = numbers.hexToInt(privateKey); 68 | return getPublic(privateKeyBigInt); 69 | } 70 | 71 | /// address calculation with privateKey for Zilliqa 72 | String getAddressFromPrivateKey(String privateKey) { 73 | String publicKey = getPubKeyFromPrivateKey(privateKey); 74 | return getAddressFromPublicKey(publicKey); 75 | } 76 | 77 | /// address calculation with publicKey for zilliqa 78 | String getAddressFromPublicKey(String publicKey) { 79 | return sha256.convert(numbers.hexToBytes(publicKey)).toString().substring(24); 80 | } 81 | 82 | BigInt hash(BigInt q, List pubkey, List msg) { 83 | final totalLength = PUBKEY_COMPRESSED_SIZE_BYTES * 2 + msg.length; 84 | 85 | /// 33 q + 33 pubkey + variable msgLen 86 | 87 | var Q = numbers.intToBytes(q); 88 | 89 | List B = List.filled(totalLength, 0); 90 | 91 | B.setRange(0, Q.length, Q); 92 | 93 | B.setRange(33, 33 + pubkey.length, pubkey); 94 | 95 | B.setRange(66, 66 + msg.length, msg); 96 | 97 | var hashByte = sha256.convert(B).bytes; 98 | 99 | return numbers.bytesToInt(hashByte); 100 | } 101 | 102 | DRBG getDRBG(List msg) { 103 | String nonce = numbers.bytesToHex(msg); 104 | 105 | DartRandom rn = new DartRandom(new Random.secure()); 106 | 107 | var entropy = rn.nextBigInteger(ENT_LEN * 8).toRadixString(16); 108 | 109 | if (entropy.length > ENT_LEN * 2) { 110 | entropy = entropy.substring(0, ENT_LEN * 2); 111 | } 112 | 113 | var randomPers = rn.nextBigInteger((ENT_LEN) * 8).toRadixString(16); 114 | 115 | if (randomPers.length > (ENT_LEN) * 2) { 116 | randomPers = randomPers.substring(0, (ENT_LEN) * 2); 117 | } 118 | var randomPerBytes = numbers.hexToBytes(randomPers); 119 | var pers = []..length = ALG_LEN + ENT_LEN; 120 | pers.fillRange(0, pers.length, 0); 121 | 122 | pers.setRange(0, randomPerBytes.length, randomPerBytes); 123 | pers.setRange(ENT_LEN, pers.length, ALG); 124 | return new DRBG( 125 | hash: sha256, entropy: entropy, nonce: nonce, pers: pers.cast()); 126 | } 127 | 128 | SchnorrSignature toSignature(List serialised) { 129 | var r = numbers.bytesToInt(serialised.sublist(0, 64)); 130 | var s = numbers.bytesToInt(serialised.sublist(64)); 131 | return new SchnorrSignature(r, s); 132 | } 133 | 134 | SchnorrSignature? trySign( 135 | List msg, BigInt k, BigInt privKey, List pubKey) { 136 | bool isZero(BigInt test) => 137 | test.compareTo(BigInt.from(0)) != 0 ? false : true; 138 | bool isGteCurve(BigInt test) => test.compareTo(params.n) > 1 ? true : false; 139 | 140 | if (isZero(privKey)) { 141 | throw 'Bad private key.'; 142 | } 143 | if (isGteCurve(privKey)) { 144 | throw 'Bad private key'; 145 | } 146 | 147 | /// 1a. check that k is not 0 148 | if (isZero(k)) { 149 | return null; 150 | } 151 | 152 | /// 1b. check that k is < the order of the group 153 | if (isGteCurve(k)) { 154 | return null; 155 | } 156 | 157 | /// 2. Compute commitment Q = kG, where g is the base point 158 | ECPoint Q = (params.G * k)!; 159 | 160 | /// convert the commitment to octets first 161 | BigInt compressedQ = numbers.bytesToInt(Q.getEncoded()); 162 | 163 | /// 3. Compute the challenge r = H(Q || pubKey || msg) 164 | /// mod reduce the r value by the order of secp256k1, n 165 | 166 | BigInt r = hash(compressedQ, pubKey, msg) % (params.n); 167 | 168 | BigInt h = r; 169 | 170 | if (isZero(h)) { 171 | return null; 172 | } 173 | 174 | /// 4. Compute s = k - r * prv 175 | /// 4a. Compute r * prv 176 | BigInt s = (h * privKey) % (params.n); 177 | 178 | /// 4b. Compute s = k - r * prv mod n 179 | s = (k - s) % (params.n); 180 | 181 | if (isZero(s)) { 182 | return null; 183 | } 184 | 185 | return new SchnorrSignature(r, s); 186 | } 187 | 188 | SchnorrSignature sign(List msg, List privKey, List pubKey) { 189 | BigInt prv = numbers.bytesToInt(privKey); 190 | DRBG drbg = getDRBG(msg); 191 | int len = numbers.intToBytes(params.n).length; 192 | var sig; 193 | 194 | while (sig == null) { 195 | var k = numbers.hexToInt(drbg.generate(len)); 196 | 197 | var trySig = trySign(msg, k, prv, pubKey)!; 198 | bool res = verify( 199 | msg, 200 | trySig.r, 201 | trySig.s, 202 | pubKey, 203 | ); 204 | if (res && isSignature(trySig.signature)) { 205 | sig = trySig; 206 | } else { 207 | sig = null; 208 | } 209 | } 210 | return sig; 211 | } 212 | 213 | bool verify(List msg, BigInt? sigR, BigInt? sigS, List key) { 214 | bool isZero(BigInt test) => 215 | test.compareTo(BigInt.from(0)) != 0 ? false : true; 216 | bool isGteCurve(BigInt test) => test.compareTo(params.n) > 1 ? true : false; 217 | 218 | var sig = new SchnorrSignature(sigR, sigS); 219 | 220 | if (isZero(sig.s!) || isZero(sig.r!)) { 221 | throw 'Invalid signature'; 222 | } 223 | 224 | if (sig.s!.isNegative || sig.r!.isNegative) { 225 | throw 'Invalid signature'; 226 | } 227 | 228 | if (isGteCurve(sig.s!) || isGteCurve(sig.r!)) { 229 | throw 'Invalid signature'; 230 | } 231 | 232 | ECPoint kpub = params.curve.decodePoint(key)!; 233 | 234 | /// 235 | /// if (!curve.validate(kpub)) { 236 | /// throw new Error('Invalid public key') 237 | /// } 238 | /// 239 | 240 | ECPoint l = (kpub * (sig.r))!; 241 | 242 | ECPoint? r = params.G * (sig.s); 243 | 244 | ECPoint Q = (l + r)!; 245 | 246 | if (Q.isInfinity) { 247 | throw 'Invalid intermediate point.'; 248 | } 249 | 250 | BigInt compressedQ = numbers.bytesToInt(Q.getEncoded()); 251 | 252 | BigInt r1 = hash(compressedQ, key, msg) % (params.n); 253 | 254 | if (isZero(r1)) { 255 | throw 'Invalid hash.'; 256 | } 257 | 258 | return r1 == sig.r; 259 | } 260 | 261 | List randomBytes(int byteLength) { 262 | DartRandom rn = new DartRandom(new Random.secure()); 263 | return rn.nextBytes(byteLength); 264 | } 265 | 266 | String randomHex(int hexLength) { 267 | return numbers.bytesToHex(randomBytes(hexLength ~/ 2)); 268 | } 269 | 270 | typedef SignedTransaction = Map Function( 271 | String privateKey, Map txnDetails); 272 | 273 | Map SchnorrSign( 274 | String privateKey, Map txnDetails) { 275 | var pubKey = getPubKeyFromPrivateKey(privateKey); 276 | Map txn = { 277 | 'version': txnDetails['version'], 278 | 'nonce': txnDetails['nonce'], 279 | 'toAddr': txnDetails['toAddr'], 280 | 'amount': txnDetails['amount'], 281 | 'pubKey': pubKey, 282 | 'gasPrice': txnDetails['gasPrice'], 283 | 'gasLimit': txnDetails['gasLimit'], 284 | 'code': txnDetails['code'] != null ? txnDetails['code'] : '', 285 | 'data': txnDetails['data'] != null ? txnDetails['data'] : '' 286 | }; 287 | 288 | Uint8List encodedTx = encodeTransactionProto(txn); 289 | 290 | SchnorrSignature signature = sign( 291 | encodedTx, numbers.hexToBytes(privateKey), numbers.hexToBytes(pubKey)); 292 | 293 | txn.addEntries([new MapEntry('signature', signature.signature)]); 294 | if (verify(encodedTx, signature.r, signature.s, numbers.hexToBytes(pubKey))) { 295 | return txn; 296 | } else { 297 | throw 'Signature verify failure'; 298 | } 299 | } 300 | -------------------------------------------------------------------------------- /lib/src/crypto/sha256digest.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'package:crypto/crypto.dart'; 3 | import 'package:crypto/src/digest_sink.dart'; 4 | import 'package:laksadart/src/utils/numbers.dart'; 5 | 6 | class SHA256 { 7 | late DigestSink ds; 8 | late ByteConversionSink sha; 9 | 10 | SHA256() { 11 | this.ds = new DigestSink(); 12 | this.sha = sha256.startChunkedConversion(ds); 13 | } 14 | 15 | SHA256 update(List bytes) { 16 | this.sha.add(bytes); 17 | return this; 18 | } 19 | 20 | List digest() { 21 | this.sha.close(); 22 | return this.ds.value.bytes; 23 | } 24 | 25 | @override 26 | String toString() { 27 | var bytes = this.digest(); 28 | return bytesToHex(bytes); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/src/crypto/signature.dart: -------------------------------------------------------------------------------- 1 | abstract class Signature {} 2 | 3 | class SchnorrSignature implements Signature { 4 | final BigInt? r; 5 | final BigInt? s; 6 | 7 | SchnorrSignature(this.r, this.s); 8 | 9 | String toString() => "(${r.toString()},${s.toString()})"; 10 | 11 | String get signature => "${r!.toRadixString(16)}" + "${s!.toRadixString(16)}"; 12 | 13 | bool operator ==(Object? other) { 14 | if (other == null) return false; 15 | if (other is! SchnorrSignature) return false; 16 | return (other.r == this.r) && (other.s == this.s); 17 | } 18 | 19 | int get hashCode { 20 | return r.hashCode + s.hashCode; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/src/data/network/network_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'network_info.freezed.dart'; 4 | part 'network_info.g.dart'; 5 | 6 | @freezed 7 | abstract class NetworkInfo implements _$NetworkInfo { 8 | const NetworkInfo._(); 9 | 10 | const factory NetworkInfo( 11 | {int? chainID, 12 | String? networkID, 13 | String? nodeProviderUrl, 14 | String? blockExplorerUrl, 15 | String? blockExplorerNetwork}) = _NetworkInfo; 16 | 17 | factory NetworkInfo.fromJson(Map json) => 18 | _$NetworkInfoFromJson(json); 19 | } 20 | -------------------------------------------------------------------------------- /lib/src/data/network/network_info.freezed.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | // ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides 3 | 4 | part of 'network_info.dart'; 5 | 6 | // ************************************************************************** 7 | // FreezedGenerator 8 | // ************************************************************************** 9 | 10 | T _$identity(T value) => value; 11 | 12 | final _privateConstructorUsedError = UnsupportedError( 13 | 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more informations: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); 14 | 15 | NetworkInfo _$NetworkInfoFromJson(Map json) { 16 | return _NetworkInfo.fromJson(json); 17 | } 18 | 19 | /// @nodoc 20 | class _$NetworkInfoTearOff { 21 | const _$NetworkInfoTearOff(); 22 | 23 | _NetworkInfo call( 24 | {int? chainID, 25 | String? networkID, 26 | String? nodeProviderUrl, 27 | String? blockExplorerUrl, 28 | String? blockExplorerNetwork}) { 29 | return _NetworkInfo( 30 | chainID: chainID, 31 | networkID: networkID, 32 | nodeProviderUrl: nodeProviderUrl, 33 | blockExplorerUrl: blockExplorerUrl, 34 | blockExplorerNetwork: blockExplorerNetwork, 35 | ); 36 | } 37 | 38 | NetworkInfo fromJson(Map json) { 39 | return NetworkInfo.fromJson(json); 40 | } 41 | } 42 | 43 | /// @nodoc 44 | const $NetworkInfo = _$NetworkInfoTearOff(); 45 | 46 | /// @nodoc 47 | mixin _$NetworkInfo { 48 | int? get chainID => throw _privateConstructorUsedError; 49 | String? get networkID => throw _privateConstructorUsedError; 50 | String? get nodeProviderUrl => throw _privateConstructorUsedError; 51 | String? get blockExplorerUrl => throw _privateConstructorUsedError; 52 | String? get blockExplorerNetwork => throw _privateConstructorUsedError; 53 | 54 | Map toJson() => throw _privateConstructorUsedError; 55 | @JsonKey(ignore: true) 56 | $NetworkInfoCopyWith get copyWith => 57 | throw _privateConstructorUsedError; 58 | } 59 | 60 | /// @nodoc 61 | abstract class $NetworkInfoCopyWith<$Res> { 62 | factory $NetworkInfoCopyWith( 63 | NetworkInfo value, $Res Function(NetworkInfo) then) = 64 | _$NetworkInfoCopyWithImpl<$Res>; 65 | $Res call( 66 | {int? chainID, 67 | String? networkID, 68 | String? nodeProviderUrl, 69 | String? blockExplorerUrl, 70 | String? blockExplorerNetwork}); 71 | } 72 | 73 | /// @nodoc 74 | class _$NetworkInfoCopyWithImpl<$Res> implements $NetworkInfoCopyWith<$Res> { 75 | _$NetworkInfoCopyWithImpl(this._value, this._then); 76 | 77 | final NetworkInfo _value; 78 | // ignore: unused_field 79 | final $Res Function(NetworkInfo) _then; 80 | 81 | @override 82 | $Res call({ 83 | Object? chainID = freezed, 84 | Object? networkID = freezed, 85 | Object? nodeProviderUrl = freezed, 86 | Object? blockExplorerUrl = freezed, 87 | Object? blockExplorerNetwork = freezed, 88 | }) { 89 | return _then(_value.copyWith( 90 | chainID: chainID == freezed 91 | ? _value.chainID 92 | : chainID // ignore: cast_nullable_to_non_nullable 93 | as int?, 94 | networkID: networkID == freezed 95 | ? _value.networkID 96 | : networkID // ignore: cast_nullable_to_non_nullable 97 | as String?, 98 | nodeProviderUrl: nodeProviderUrl == freezed 99 | ? _value.nodeProviderUrl 100 | : nodeProviderUrl // ignore: cast_nullable_to_non_nullable 101 | as String?, 102 | blockExplorerUrl: blockExplorerUrl == freezed 103 | ? _value.blockExplorerUrl 104 | : blockExplorerUrl // ignore: cast_nullable_to_non_nullable 105 | as String?, 106 | blockExplorerNetwork: blockExplorerNetwork == freezed 107 | ? _value.blockExplorerNetwork 108 | : blockExplorerNetwork // ignore: cast_nullable_to_non_nullable 109 | as String?, 110 | )); 111 | } 112 | } 113 | 114 | /// @nodoc 115 | abstract class _$NetworkInfoCopyWith<$Res> 116 | implements $NetworkInfoCopyWith<$Res> { 117 | factory _$NetworkInfoCopyWith( 118 | _NetworkInfo value, $Res Function(_NetworkInfo) then) = 119 | __$NetworkInfoCopyWithImpl<$Res>; 120 | @override 121 | $Res call( 122 | {int? chainID, 123 | String? networkID, 124 | String? nodeProviderUrl, 125 | String? blockExplorerUrl, 126 | String? blockExplorerNetwork}); 127 | } 128 | 129 | /// @nodoc 130 | class __$NetworkInfoCopyWithImpl<$Res> extends _$NetworkInfoCopyWithImpl<$Res> 131 | implements _$NetworkInfoCopyWith<$Res> { 132 | __$NetworkInfoCopyWithImpl( 133 | _NetworkInfo _value, $Res Function(_NetworkInfo) _then) 134 | : super(_value, (v) => _then(v as _NetworkInfo)); 135 | 136 | @override 137 | _NetworkInfo get _value => super._value as _NetworkInfo; 138 | 139 | @override 140 | $Res call({ 141 | Object? chainID = freezed, 142 | Object? networkID = freezed, 143 | Object? nodeProviderUrl = freezed, 144 | Object? blockExplorerUrl = freezed, 145 | Object? blockExplorerNetwork = freezed, 146 | }) { 147 | return _then(_NetworkInfo( 148 | chainID: chainID == freezed 149 | ? _value.chainID 150 | : chainID // ignore: cast_nullable_to_non_nullable 151 | as int?, 152 | networkID: networkID == freezed 153 | ? _value.networkID 154 | : networkID // ignore: cast_nullable_to_non_nullable 155 | as String?, 156 | nodeProviderUrl: nodeProviderUrl == freezed 157 | ? _value.nodeProviderUrl 158 | : nodeProviderUrl // ignore: cast_nullable_to_non_nullable 159 | as String?, 160 | blockExplorerUrl: blockExplorerUrl == freezed 161 | ? _value.blockExplorerUrl 162 | : blockExplorerUrl // ignore: cast_nullable_to_non_nullable 163 | as String?, 164 | blockExplorerNetwork: blockExplorerNetwork == freezed 165 | ? _value.blockExplorerNetwork 166 | : blockExplorerNetwork // ignore: cast_nullable_to_non_nullable 167 | as String?, 168 | )); 169 | } 170 | } 171 | 172 | /// @nodoc 173 | @JsonSerializable() 174 | class _$_NetworkInfo extends _NetworkInfo { 175 | const _$_NetworkInfo( 176 | {this.chainID, 177 | this.networkID, 178 | this.nodeProviderUrl, 179 | this.blockExplorerUrl, 180 | this.blockExplorerNetwork}) 181 | : super._(); 182 | 183 | factory _$_NetworkInfo.fromJson(Map json) => 184 | _$_$_NetworkInfoFromJson(json); 185 | 186 | @override 187 | final int? chainID; 188 | @override 189 | final String? networkID; 190 | @override 191 | final String? nodeProviderUrl; 192 | @override 193 | final String? blockExplorerUrl; 194 | @override 195 | final String? blockExplorerNetwork; 196 | 197 | @override 198 | String toString() { 199 | return 'NetworkInfo(chainID: $chainID, networkID: $networkID, nodeProviderUrl: $nodeProviderUrl, blockExplorerUrl: $blockExplorerUrl, blockExplorerNetwork: $blockExplorerNetwork)'; 200 | } 201 | 202 | @override 203 | bool operator ==(dynamic other) { 204 | return identical(this, other) || 205 | (other is _NetworkInfo && 206 | (identical(other.chainID, chainID) || 207 | const DeepCollectionEquality() 208 | .equals(other.chainID, chainID)) && 209 | (identical(other.networkID, networkID) || 210 | const DeepCollectionEquality() 211 | .equals(other.networkID, networkID)) && 212 | (identical(other.nodeProviderUrl, nodeProviderUrl) || 213 | const DeepCollectionEquality() 214 | .equals(other.nodeProviderUrl, nodeProviderUrl)) && 215 | (identical(other.blockExplorerUrl, blockExplorerUrl) || 216 | const DeepCollectionEquality() 217 | .equals(other.blockExplorerUrl, blockExplorerUrl)) && 218 | (identical(other.blockExplorerNetwork, blockExplorerNetwork) || 219 | const DeepCollectionEquality() 220 | .equals(other.blockExplorerNetwork, blockExplorerNetwork))); 221 | } 222 | 223 | @override 224 | int get hashCode => 225 | runtimeType.hashCode ^ 226 | const DeepCollectionEquality().hash(chainID) ^ 227 | const DeepCollectionEquality().hash(networkID) ^ 228 | const DeepCollectionEquality().hash(nodeProviderUrl) ^ 229 | const DeepCollectionEquality().hash(blockExplorerUrl) ^ 230 | const DeepCollectionEquality().hash(blockExplorerNetwork); 231 | 232 | @JsonKey(ignore: true) 233 | @override 234 | _$NetworkInfoCopyWith<_NetworkInfo> get copyWith => 235 | __$NetworkInfoCopyWithImpl<_NetworkInfo>(this, _$identity); 236 | 237 | @override 238 | Map toJson() { 239 | return _$_$_NetworkInfoToJson(this); 240 | } 241 | } 242 | 243 | abstract class _NetworkInfo extends NetworkInfo { 244 | const factory _NetworkInfo( 245 | {int? chainID, 246 | String? networkID, 247 | String? nodeProviderUrl, 248 | String? blockExplorerUrl, 249 | String? blockExplorerNetwork}) = _$_NetworkInfo; 250 | const _NetworkInfo._() : super._(); 251 | 252 | factory _NetworkInfo.fromJson(Map json) = 253 | _$_NetworkInfo.fromJson; 254 | 255 | @override 256 | int? get chainID => throw _privateConstructorUsedError; 257 | @override 258 | String? get networkID => throw _privateConstructorUsedError; 259 | @override 260 | String? get nodeProviderUrl => throw _privateConstructorUsedError; 261 | @override 262 | String? get blockExplorerUrl => throw _privateConstructorUsedError; 263 | @override 264 | String? get blockExplorerNetwork => throw _privateConstructorUsedError; 265 | @override 266 | @JsonKey(ignore: true) 267 | _$NetworkInfoCopyWith<_NetworkInfo> get copyWith => 268 | throw _privateConstructorUsedError; 269 | } 270 | -------------------------------------------------------------------------------- /lib/src/data/network/network_info.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'network_info.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _$_NetworkInfo _$_$_NetworkInfoFromJson(Map json) { 10 | return _$_NetworkInfo( 11 | chainID: json['chainID'] as int?, 12 | networkID: json['networkID'] as String?, 13 | nodeProviderUrl: json['nodeProviderUrl'] as String?, 14 | blockExplorerUrl: json['blockExplorerUrl'] as String?, 15 | blockExplorerNetwork: json['blockExplorerNetwork'] as String?, 16 | ); 17 | } 18 | 19 | Map _$_$_NetworkInfoToJson(_$_NetworkInfo instance) => 20 | { 21 | 'chainID': instance.chainID, 22 | 'networkID': instance.networkID, 23 | 'nodeProviderUrl': instance.nodeProviderUrl, 24 | 'blockExplorerUrl': instance.blockExplorerUrl, 25 | 'blockExplorerNetwork': instance.blockExplorerNetwork, 26 | }; 27 | -------------------------------------------------------------------------------- /lib/src/messenger/Blockchain.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:convert'; 3 | import 'package:laksadart/src/provider/net.dart'; 4 | import 'package:laksadart/src/core/zilliqa_module.dart'; 5 | import 'package:laksadart/src/provider/Middleware.dart'; 6 | import 'package:laksadart/src/transaction/transaction.dart'; 7 | import 'package:laksadart/src/transaction/api.dart'; 8 | import 'messenger.dart'; 9 | 10 | class Blockchain implements ZilliqaModule { 11 | Messenger? _messenger; 12 | Blockchain(this._messenger); 13 | 14 | @override 15 | void set messenger(Messenger? messenger) => this._messenger = messenger; 16 | 17 | @override 18 | Messenger get messenger => this._messenger!; 19 | 20 | Future getBalance({String? address}) async { 21 | this.messenger.setMiddleware((data) => new RPCMiddleWare(data).raw, 22 | match: RPCMethod.GetBalance); 23 | return await this.messenger.send(RPCMethod.GetBalance, address); 24 | } 25 | 26 | Future getBlockchainInfo() async { 27 | return await this.messenger.send(RPCMethod.GetBlockchainInfo); 28 | } 29 | 30 | Future getDSBlock({int? blockNum}) async { 31 | return await this.messenger.send(RPCMethod.GetDSBlock, blockNum.toString()); 32 | } 33 | 34 | Future getLatestDSBlock() async { 35 | return await this.messenger.send(RPCMethod.GetLatestDSBlock); 36 | } 37 | 38 | Future getNumDSBlocks() async { 39 | return await this.messenger.send(RPCMethod.GetNumDSBlocks); 40 | } 41 | 42 | Future getDSBlockRate() async { 43 | return await this.messenger.send(RPCMethod.GetDSBlockRate); 44 | } 45 | 46 | Future getDSBlockListing({int? max}) async { 47 | return await this.messenger.send(RPCMethod.DSBlockListing, max); 48 | } 49 | 50 | Future getTxBlock({int? blockNum}) async { 51 | return await this.messenger.send(RPCMethod.GetTxBlock, blockNum.toString()); 52 | } 53 | 54 | Future getLatestTxBlock() async { 55 | return await this.messenger.send(RPCMethod.GetLatestTxBlock); 56 | } 57 | 58 | Future getNumTxBlocks() async { 59 | return await this.messenger.send(RPCMethod.GetNumTxBlocks); 60 | } 61 | 62 | Future getTxBlockRate() async { 63 | return await this.messenger.send(RPCMethod.GetTxBlockRate); 64 | } 65 | 66 | Future getTxBlockListing({int? max}) async { 67 | return await this.messenger.send(RPCMethod.TxBlockListing, max); 68 | } 69 | 70 | Future getNumTransactions() async { 71 | return await this.messenger.send(RPCMethod.GetNumTransactions); 72 | } 73 | 74 | Future getTransactionRate() async { 75 | return await this.messenger.send(RPCMethod.GetTransactionRate); 76 | } 77 | 78 | Future getCurrentMiniEpoch() async { 79 | return await this.messenger.send(RPCMethod.GetCurrentMiniEpoch); 80 | } 81 | 82 | Future getCurrentDSEpoch() async { 83 | return await this.messenger.send(RPCMethod.GetCurrentDSEpoch); 84 | } 85 | 86 | Future getPrevDifficulty() async { 87 | return await this.messenger.send(RPCMethod.GetPrevDifficulty); 88 | } 89 | 90 | Future getPrevDSDifficulty() async { 91 | return await this.messenger.send(RPCMethod.GetPrevDSDifficulty); 92 | } 93 | 94 | Future getRecentTransactions() async { 95 | return await this.messenger.send(RPCMethod.GetRecentTransactions); 96 | } 97 | 98 | Future getNumTxnsTxEpoch({int? epoch}) async { 99 | return await this.messenger.send(RPCMethod.GetNumTxnsTxEpoch, epoch); 100 | } 101 | 102 | Future getNumTxnsDSEpoch({int? epoch}) async { 103 | return await this.messenger.send(RPCMethod.GetNumTxnsDSEpoch, epoch); 104 | } 105 | 106 | Future getMinimumGasPrice() async { 107 | return await this.messenger.send(RPCMethod.GetMinimumGasPrice); 108 | } 109 | 110 | Future getTransactionsForTxBlock({int? txBlock}) async { 111 | return await this 112 | .messenger 113 | .send(RPCMethod.GetTransactionsForTxBlock, txBlock); 114 | } 115 | 116 | Future createTransaction(Transaction transaction) async { 117 | try { 118 | var res = await this 119 | .messenger 120 | .send(RPCMethod.CreateTransaction, transaction.toPayload); 121 | if (res.result != null) { 122 | var result = res.result!.toMap()!; 123 | var transactionID = result['TranID']; 124 | if (transactionID == null) { 125 | throw 'Transaction fail'; 126 | } else { 127 | transaction.transactionID = transactionID; 128 | transaction.status = TxStatus.Pending; 129 | return new TransactionSent(transaction, result); 130 | } 131 | } else if (res.error != null) { 132 | throw res.error!.message!; 133 | } 134 | return null; 135 | } catch (error) { 136 | rethrow; 137 | } 138 | } 139 | 140 | Future completeTransaction( 141 | {required Transaction transaction, 142 | int? maxAttempts, 143 | int? interval}) async { 144 | try { 145 | TransactionSent? result = await createTransaction(transaction); 146 | Transaction confirmed = await result!.transaction.confirm( 147 | txHash: result.transaction.transactionID, 148 | maxAttempts: maxAttempts, 149 | interval: interval); 150 | return confirmed; 151 | } catch (error) { 152 | rethrow; 153 | } 154 | } 155 | 156 | Future getTransaction({String? txHash}) async { 157 | return await this.messenger.send(RPCMethod.GetTransaction, txHash); 158 | } 159 | 160 | // scilla-runner checker 161 | Future checkCode({String? code}) async { 162 | return await this 163 | .messenger 164 | .sendServer(Endpoint.ScillaCheck, {'code': code}); 165 | } 166 | 167 | // scilla-runner call 168 | Future testCall(Map callJson) async { 169 | return await this.messenger.sendServer(Endpoint.ScillaCall, { 170 | //don't encode code, provider will encode it automatically 171 | 'code': callJson['code'].toString(), 172 | 'init': json.encode(callJson['init']), 173 | 'blockchain': json.encode(callJson['blockchain']), 174 | 'gaslimit': json.encode(callJson['gasLimit']) 175 | }); 176 | } 177 | 178 | Future get clientVersion async => 179 | await this.messenger.send(RPCMethod.GetClientVersion); 180 | 181 | Future get networkID async => 182 | await this.messenger.send(RPCMethod.GetNetworkId); 183 | 184 | Future get protocolVersion async => 185 | await this.messenger.send(RPCMethod.GetProtocolVersion); 186 | } 187 | -------------------------------------------------------------------------------- /lib/src/messenger/Messenger.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:laksadart/src/provider/Http.dart'; 3 | import 'package:laksadart/src/provider/net.dart'; 4 | import 'package:laksadart/src/provider/Middleware.dart'; 5 | 6 | class Messenger { 7 | HttpProvider? nodeProvider; 8 | HttpProvider? scillaProvider; 9 | String? _networkID; 10 | 11 | String? get nodeUrl => this.nodeProvider!.url; 12 | String? get scillaUrl => this.scillaProvider!.url; 13 | void set networkID(String? networkID) => this._networkID = networkID; 14 | String? get networkID => this._networkID; 15 | 16 | Function middleware = (data) => new RPCMiddleWare(data); 17 | String? middlewareApply = '*'; 18 | 19 | Messenger( 20 | {HttpProvider? nodeProvider, 21 | HttpProvider? scillaProvider, 22 | String? networkID}) { 23 | this.nodeProvider = nodeProvider is HttpProvider ? nodeProvider : null; 24 | this.scillaProvider = 25 | scillaProvider is HttpProvider ? scillaProvider : this.nodeProvider; 26 | this._networkID = networkID; 27 | } 28 | void setNodeProvider(HttpProvider provider) { 29 | this.nodeProvider = provider; 30 | } 31 | 32 | void setScillaProvider(HttpProvider provider) { 33 | this.scillaProvider = provider; 34 | } 35 | 36 | void setMiddleware(Function middware, {String? match}) { 37 | this.middleware = middware; 38 | this.middlewareApply = match; 39 | } 40 | 41 | void useMiddleware(Function middleware, {String? match}) { 42 | this.nodeProvider!.middleware.response.use(middleware, match: match); 43 | } 44 | 45 | Future send(String method, [dynamic params]) async { 46 | // use middleware 47 | 48 | var methodMap = new RPCMethod().Mapping; 49 | if (methodMap[method] == null) { 50 | throw '${method} is not found in RPCMethod list'; 51 | } else { 52 | return await this.nodeProvider!.send(method, params); 53 | } 54 | } 55 | 56 | Future sendServer(String endpoint, [dynamic params]) async { 57 | // use middleware 58 | 59 | var methodMap = new Endpoint().Mapping; 60 | if (methodMap[endpoint] == null) { 61 | throw '${endpoint} is not found in Endpoint list'; 62 | } else { 63 | return await this.scillaProvider!.sendServer(endpoint, params); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /lib/src/messenger/index.dart: -------------------------------------------------------------------------------- 1 | library messenger; 2 | 3 | export 'blockchain.dart'; 4 | export 'messenger.dart'; 5 | -------------------------------------------------------------------------------- /lib/src/proto/message.pb.dart: -------------------------------------------------------------------------------- 1 | /// 2 | // Generated code. Do not modify. 3 | // source: message.proto 4 | // 5 | 6 | // ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields 7 | 8 | import 'dart:core' as $core; 9 | 10 | import 'package:fixnum/fixnum.dart' as $fixnum; 11 | import 'package:protobuf/protobuf.dart' as $pb; 12 | 13 | class ByteArray extends $pb.GeneratedMessage { 14 | static final $pb.BuilderInfo _i = $pb.BuilderInfo( 15 | const $core.bool.fromEnvironment('protobuf.omit_message_names') 16 | ? '' 17 | : 'ByteArray', 18 | package: const $pb.PackageName( 19 | const $core.bool.fromEnvironment('protobuf.omit_message_names') 20 | ? '' 21 | : 'ZilliqaMessage'), 22 | createEmptyInstance: create) 23 | ..a<$core.List<$core.int>>( 24 | 1, 25 | const $core.bool.fromEnvironment('protobuf.omit_field_names') 26 | ? '' 27 | : 'data', 28 | $pb.PbFieldType.QY); 29 | 30 | ByteArray._() : super(); 31 | factory ByteArray() => create(); 32 | factory ByteArray.fromBuffer($core.List<$core.int> i, 33 | [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => 34 | create()..mergeFromBuffer(i, r); 35 | factory ByteArray.fromJson($core.String i, 36 | [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => 37 | create()..mergeFromJson(i, r); 38 | @$core.Deprecated('Using this can add significant overhead to your binary. ' 39 | 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 40 | 'Will be removed in next major version') 41 | ByteArray clone() => ByteArray()..mergeFromMessage(this); 42 | @$core.Deprecated('Using this can add significant overhead to your binary. ' 43 | 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 44 | 'Will be removed in next major version') 45 | ByteArray copyWith(void Function(ByteArray) updates) => 46 | super.copyWith((message) => updates(message as ByteArray)) 47 | as ByteArray; // ignore: deprecated_member_use 48 | $pb.BuilderInfo get info_ => _i; 49 | @$core.pragma('dart2js:noInline') 50 | static ByteArray create() => ByteArray._(); 51 | ByteArray createEmptyInstance() => create(); 52 | static $pb.PbList createRepeated() => $pb.PbList(); 53 | @$core.pragma('dart2js:noInline') 54 | static ByteArray getDefault() => 55 | _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); 56 | static ByteArray? _defaultInstance; 57 | 58 | @$pb.TagNumber(1) 59 | $core.List<$core.int> get data => $_getN(0); 60 | @$pb.TagNumber(1) 61 | set data($core.List<$core.int> v) { 62 | $_setBytes(0, v); 63 | } 64 | 65 | @$pb.TagNumber(1) 66 | $core.bool hasData() => $_has(0); 67 | @$pb.TagNumber(1) 68 | void clearData() => clearField(1); 69 | } 70 | 71 | class ProtoTransactionCoreInfo extends $pb.GeneratedMessage { 72 | static final $pb.BuilderInfo _i = $pb.BuilderInfo( 73 | const $core.bool.fromEnvironment('protobuf.omit_message_names') 74 | ? '' 75 | : 'ProtoTransactionCoreInfo', 76 | package: const $pb.PackageName( 77 | const $core.bool.fromEnvironment('protobuf.omit_message_names') 78 | ? '' 79 | : 'ZilliqaMessage'), 80 | createEmptyInstance: create) 81 | ..a<$core.int>( 82 | 1, 83 | const $core.bool.fromEnvironment('protobuf.omit_field_names') 84 | ? '' 85 | : 'version', 86 | $pb.PbFieldType.OU3) 87 | ..a<$fixnum.Int64>( 88 | 2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'nonce', $pb.PbFieldType.OU6, 89 | defaultOrMaker: $fixnum.Int64.ZERO) 90 | ..a<$core.List<$core.int>>( 91 | 3, 92 | const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'toaddr', 93 | $pb.PbFieldType.OY) 94 | ..aOM(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'senderpubkey', subBuilder: ByteArray.create) 95 | ..aOM(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'amount', subBuilder: ByteArray.create) 96 | ..aOM(6, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gasprice', subBuilder: ByteArray.create) 97 | ..a<$fixnum.Int64>(7, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gaslimit', $pb.PbFieldType.OU6, defaultOrMaker: $fixnum.Int64.ZERO) 98 | ..a<$core.List<$core.int>>(8, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'code', $pb.PbFieldType.OY) 99 | ..a<$core.List<$core.int>>(9, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'data', $pb.PbFieldType.OY); 100 | 101 | ProtoTransactionCoreInfo._() : super(); 102 | factory ProtoTransactionCoreInfo() => create(); 103 | factory ProtoTransactionCoreInfo.fromBuffer($core.List<$core.int> i, 104 | [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => 105 | create()..mergeFromBuffer(i, r); 106 | factory ProtoTransactionCoreInfo.fromJson($core.String i, 107 | [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => 108 | create()..mergeFromJson(i, r); 109 | @$core.Deprecated('Using this can add significant overhead to your binary. ' 110 | 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 111 | 'Will be removed in next major version') 112 | ProtoTransactionCoreInfo clone() => 113 | ProtoTransactionCoreInfo()..mergeFromMessage(this); 114 | @$core.Deprecated('Using this can add significant overhead to your binary. ' 115 | 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 116 | 'Will be removed in next major version') 117 | ProtoTransactionCoreInfo copyWith( 118 | void Function(ProtoTransactionCoreInfo) updates) => 119 | super.copyWith((message) => updates(message as ProtoTransactionCoreInfo)) 120 | as ProtoTransactionCoreInfo; // ignore: deprecated_member_use 121 | $pb.BuilderInfo get info_ => _i; 122 | @$core.pragma('dart2js:noInline') 123 | static ProtoTransactionCoreInfo create() => ProtoTransactionCoreInfo._(); 124 | ProtoTransactionCoreInfo createEmptyInstance() => create(); 125 | static $pb.PbList createRepeated() => 126 | $pb.PbList(); 127 | @$core.pragma('dart2js:noInline') 128 | static ProtoTransactionCoreInfo getDefault() => _defaultInstance ??= 129 | $pb.GeneratedMessage.$_defaultFor(create); 130 | static ProtoTransactionCoreInfo? _defaultInstance; 131 | 132 | @$pb.TagNumber(1) 133 | $core.int get version => $_getIZ(0); 134 | @$pb.TagNumber(1) 135 | set version($core.int v) { 136 | $_setUnsignedInt32(0, v); 137 | } 138 | 139 | @$pb.TagNumber(1) 140 | $core.bool hasVersion() => $_has(0); 141 | @$pb.TagNumber(1) 142 | void clearVersion() => clearField(1); 143 | 144 | @$pb.TagNumber(2) 145 | $fixnum.Int64 get nonce => $_getI64(1); 146 | @$pb.TagNumber(2) 147 | set nonce($fixnum.Int64 v) { 148 | $_setInt64(1, v); 149 | } 150 | 151 | @$pb.TagNumber(2) 152 | $core.bool hasNonce() => $_has(1); 153 | @$pb.TagNumber(2) 154 | void clearNonce() => clearField(2); 155 | 156 | @$pb.TagNumber(3) 157 | $core.List<$core.int> get toaddr => $_getN(2); 158 | @$pb.TagNumber(3) 159 | set toaddr($core.List<$core.int> v) { 160 | $_setBytes(2, v); 161 | } 162 | 163 | @$pb.TagNumber(3) 164 | $core.bool hasToaddr() => $_has(2); 165 | @$pb.TagNumber(3) 166 | void clearToaddr() => clearField(3); 167 | 168 | @$pb.TagNumber(4) 169 | ByteArray get senderpubkey => $_getN(3); 170 | @$pb.TagNumber(4) 171 | set senderpubkey(ByteArray v) { 172 | setField(4, v); 173 | } 174 | 175 | @$pb.TagNumber(4) 176 | $core.bool hasSenderpubkey() => $_has(3); 177 | @$pb.TagNumber(4) 178 | void clearSenderpubkey() => clearField(4); 179 | @$pb.TagNumber(4) 180 | ByteArray ensureSenderpubkey() => $_ensure(3); 181 | 182 | @$pb.TagNumber(5) 183 | ByteArray get amount => $_getN(4); 184 | @$pb.TagNumber(5) 185 | set amount(ByteArray v) { 186 | setField(5, v); 187 | } 188 | 189 | @$pb.TagNumber(5) 190 | $core.bool hasAmount() => $_has(4); 191 | @$pb.TagNumber(5) 192 | void clearAmount() => clearField(5); 193 | @$pb.TagNumber(5) 194 | ByteArray ensureAmount() => $_ensure(4); 195 | 196 | @$pb.TagNumber(6) 197 | ByteArray get gasprice => $_getN(5); 198 | @$pb.TagNumber(6) 199 | set gasprice(ByteArray v) { 200 | setField(6, v); 201 | } 202 | 203 | @$pb.TagNumber(6) 204 | $core.bool hasGasprice() => $_has(5); 205 | @$pb.TagNumber(6) 206 | void clearGasprice() => clearField(6); 207 | @$pb.TagNumber(6) 208 | ByteArray ensureGasprice() => $_ensure(5); 209 | 210 | @$pb.TagNumber(7) 211 | $fixnum.Int64 get gaslimit => $_getI64(6); 212 | @$pb.TagNumber(7) 213 | set gaslimit($fixnum.Int64 v) { 214 | $_setInt64(6, v); 215 | } 216 | 217 | @$pb.TagNumber(7) 218 | $core.bool hasGaslimit() => $_has(6); 219 | @$pb.TagNumber(7) 220 | void clearGaslimit() => clearField(7); 221 | 222 | @$pb.TagNumber(8) 223 | $core.List<$core.int> get code => $_getN(7); 224 | @$pb.TagNumber(8) 225 | set code($core.List<$core.int> v) { 226 | $_setBytes(7, v); 227 | } 228 | 229 | @$pb.TagNumber(8) 230 | $core.bool hasCode() => $_has(7); 231 | @$pb.TagNumber(8) 232 | void clearCode() => clearField(8); 233 | 234 | @$pb.TagNumber(9) 235 | $core.List<$core.int> get data => $_getN(8); 236 | @$pb.TagNumber(9) 237 | set data($core.List<$core.int> v) { 238 | $_setBytes(8, v); 239 | } 240 | 241 | @$pb.TagNumber(9) 242 | $core.bool hasData() => $_has(8); 243 | @$pb.TagNumber(9) 244 | void clearData() => clearField(9); 245 | } 246 | 247 | class ProtoTransaction extends $pb.GeneratedMessage { 248 | static final $pb.BuilderInfo _i = $pb.BuilderInfo( 249 | const $core.bool.fromEnvironment('protobuf.omit_message_names') 250 | ? '' 251 | : 'ProtoTransaction', 252 | package: const $pb.PackageName( 253 | const $core.bool.fromEnvironment('protobuf.omit_message_names') 254 | ? '' 255 | : 'ZilliqaMessage'), 256 | createEmptyInstance: create) 257 | ..a<$core.List<$core.int>>( 258 | 1, 259 | const $core.bool.fromEnvironment('protobuf.omit_field_names') 260 | ? '' 261 | : 'tranid', 262 | $pb.PbFieldType.OY) 263 | ..aOM( 264 | 2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'info', 265 | subBuilder: ProtoTransactionCoreInfo.create) 266 | ..aOM( 267 | 3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'signature', 268 | subBuilder: ByteArray.create); 269 | 270 | ProtoTransaction._() : super(); 271 | factory ProtoTransaction() => create(); 272 | factory ProtoTransaction.fromBuffer($core.List<$core.int> i, 273 | [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => 274 | create()..mergeFromBuffer(i, r); 275 | factory ProtoTransaction.fromJson($core.String i, 276 | [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => 277 | create()..mergeFromJson(i, r); 278 | @$core.Deprecated('Using this can add significant overhead to your binary. ' 279 | 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 280 | 'Will be removed in next major version') 281 | ProtoTransaction clone() => ProtoTransaction()..mergeFromMessage(this); 282 | @$core.Deprecated('Using this can add significant overhead to your binary. ' 283 | 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 284 | 'Will be removed in next major version') 285 | ProtoTransaction copyWith(void Function(ProtoTransaction) updates) => 286 | super.copyWith((message) => updates(message as ProtoTransaction)) 287 | as ProtoTransaction; // ignore: deprecated_member_use 288 | $pb.BuilderInfo get info_ => _i; 289 | @$core.pragma('dart2js:noInline') 290 | static ProtoTransaction create() => ProtoTransaction._(); 291 | ProtoTransaction createEmptyInstance() => create(); 292 | static $pb.PbList createRepeated() => 293 | $pb.PbList(); 294 | @$core.pragma('dart2js:noInline') 295 | static ProtoTransaction getDefault() => _defaultInstance ??= 296 | $pb.GeneratedMessage.$_defaultFor(create); 297 | static ProtoTransaction? _defaultInstance; 298 | 299 | @$pb.TagNumber(1) 300 | $core.List<$core.int> get tranid => $_getN(0); 301 | @$pb.TagNumber(1) 302 | set tranid($core.List<$core.int> v) { 303 | $_setBytes(0, v); 304 | } 305 | 306 | @$pb.TagNumber(1) 307 | $core.bool hasTranid() => $_has(0); 308 | @$pb.TagNumber(1) 309 | void clearTranid() => clearField(1); 310 | 311 | @$pb.TagNumber(2) 312 | ProtoTransactionCoreInfo get info => $_getN(1); 313 | @$pb.TagNumber(2) 314 | set info(ProtoTransactionCoreInfo v) { 315 | setField(2, v); 316 | } 317 | 318 | @$pb.TagNumber(2) 319 | $core.bool hasInfo() => $_has(1); 320 | @$pb.TagNumber(2) 321 | void clearInfo() => clearField(2); 322 | @$pb.TagNumber(2) 323 | ProtoTransactionCoreInfo ensureInfo() => $_ensure(1); 324 | 325 | @$pb.TagNumber(3) 326 | ByteArray get signature => $_getN(2); 327 | @$pb.TagNumber(3) 328 | set signature(ByteArray v) { 329 | setField(3, v); 330 | } 331 | 332 | @$pb.TagNumber(3) 333 | $core.bool hasSignature() => $_has(2); 334 | @$pb.TagNumber(3) 335 | void clearSignature() => clearField(3); 336 | @$pb.TagNumber(3) 337 | ByteArray ensureSignature() => $_ensure(2); 338 | } 339 | 340 | class ProtoTransactionReceipt extends $pb.GeneratedMessage { 341 | static final $pb.BuilderInfo _i = $pb.BuilderInfo( 342 | const $core.bool.fromEnvironment('protobuf.omit_message_names') 343 | ? '' 344 | : 'ProtoTransactionReceipt', 345 | package: const $pb.PackageName( 346 | const $core.bool.fromEnvironment('protobuf.omit_message_names') 347 | ? '' 348 | : 'ZilliqaMessage'), 349 | createEmptyInstance: create) 350 | ..a<$core.List<$core.int>>( 351 | 1, 352 | const $core.bool.fromEnvironment('protobuf.omit_field_names') 353 | ? '' 354 | : 'receipt', 355 | $pb.PbFieldType.OY) 356 | ..a<$fixnum.Int64>( 357 | 2, 358 | const $core.bool.fromEnvironment('protobuf.omit_field_names') 359 | ? '' 360 | : 'cumgas', 361 | $pb.PbFieldType.OU6, 362 | defaultOrMaker: $fixnum.Int64.ZERO) 363 | ..hasRequiredFields = false; 364 | 365 | ProtoTransactionReceipt._() : super(); 366 | factory ProtoTransactionReceipt() => create(); 367 | factory ProtoTransactionReceipt.fromBuffer($core.List<$core.int> i, 368 | [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => 369 | create()..mergeFromBuffer(i, r); 370 | factory ProtoTransactionReceipt.fromJson($core.String i, 371 | [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => 372 | create()..mergeFromJson(i, r); 373 | @$core.Deprecated('Using this can add significant overhead to your binary. ' 374 | 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 375 | 'Will be removed in next major version') 376 | ProtoTransactionReceipt clone() => 377 | ProtoTransactionReceipt()..mergeFromMessage(this); 378 | @$core.Deprecated('Using this can add significant overhead to your binary. ' 379 | 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 380 | 'Will be removed in next major version') 381 | ProtoTransactionReceipt copyWith( 382 | void Function(ProtoTransactionReceipt) updates) => 383 | super.copyWith((message) => updates(message as ProtoTransactionReceipt)) 384 | as ProtoTransactionReceipt; // ignore: deprecated_member_use 385 | $pb.BuilderInfo get info_ => _i; 386 | @$core.pragma('dart2js:noInline') 387 | static ProtoTransactionReceipt create() => ProtoTransactionReceipt._(); 388 | ProtoTransactionReceipt createEmptyInstance() => create(); 389 | static $pb.PbList createRepeated() => 390 | $pb.PbList(); 391 | @$core.pragma('dart2js:noInline') 392 | static ProtoTransactionReceipt getDefault() => _defaultInstance ??= 393 | $pb.GeneratedMessage.$_defaultFor(create); 394 | static ProtoTransactionReceipt? _defaultInstance; 395 | 396 | @$pb.TagNumber(1) 397 | $core.List<$core.int> get receipt => $_getN(0); 398 | @$pb.TagNumber(1) 399 | set receipt($core.List<$core.int> v) { 400 | $_setBytes(0, v); 401 | } 402 | 403 | @$pb.TagNumber(1) 404 | $core.bool hasReceipt() => $_has(0); 405 | @$pb.TagNumber(1) 406 | void clearReceipt() => clearField(1); 407 | 408 | @$pb.TagNumber(2) 409 | $fixnum.Int64 get cumgas => $_getI64(1); 410 | @$pb.TagNumber(2) 411 | set cumgas($fixnum.Int64 v) { 412 | $_setInt64(1, v); 413 | } 414 | 415 | @$pb.TagNumber(2) 416 | $core.bool hasCumgas() => $_has(1); 417 | @$pb.TagNumber(2) 418 | void clearCumgas() => clearField(2); 419 | } 420 | 421 | class ProtoTransactionWithReceipt extends $pb.GeneratedMessage { 422 | static final $pb.BuilderInfo _i = $pb.BuilderInfo( 423 | const $core.bool.fromEnvironment('protobuf.omit_message_names') 424 | ? '' 425 | : 'ProtoTransactionWithReceipt', 426 | package: const $pb.PackageName( 427 | const $core.bool.fromEnvironment('protobuf.omit_message_names') 428 | ? '' 429 | : 'ZilliqaMessage'), 430 | createEmptyInstance: create) 431 | ..aOM( 432 | 1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'transaction', 433 | subBuilder: ProtoTransaction.create) 434 | ..aOM(2, 435 | const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'receipt', 436 | subBuilder: ProtoTransactionReceipt.create); 437 | 438 | ProtoTransactionWithReceipt._() : super(); 439 | factory ProtoTransactionWithReceipt() => create(); 440 | factory ProtoTransactionWithReceipt.fromBuffer($core.List<$core.int> i, 441 | [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => 442 | create()..mergeFromBuffer(i, r); 443 | factory ProtoTransactionWithReceipt.fromJson($core.String i, 444 | [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => 445 | create()..mergeFromJson(i, r); 446 | @$core.Deprecated('Using this can add significant overhead to your binary. ' 447 | 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 448 | 'Will be removed in next major version') 449 | ProtoTransactionWithReceipt clone() => 450 | ProtoTransactionWithReceipt()..mergeFromMessage(this); 451 | @$core.Deprecated('Using this can add significant overhead to your binary. ' 452 | 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 453 | 'Will be removed in next major version') 454 | ProtoTransactionWithReceipt copyWith( 455 | void Function(ProtoTransactionWithReceipt) updates) => 456 | super.copyWith( 457 | (message) => updates(message as ProtoTransactionWithReceipt)) 458 | as ProtoTransactionWithReceipt; // ignore: deprecated_member_use 459 | $pb.BuilderInfo get info_ => _i; 460 | @$core.pragma('dart2js:noInline') 461 | static ProtoTransactionWithReceipt create() => 462 | ProtoTransactionWithReceipt._(); 463 | ProtoTransactionWithReceipt createEmptyInstance() => create(); 464 | static $pb.PbList createRepeated() => 465 | $pb.PbList(); 466 | @$core.pragma('dart2js:noInline') 467 | static ProtoTransactionWithReceipt getDefault() => _defaultInstance ??= 468 | $pb.GeneratedMessage.$_defaultFor(create); 469 | static ProtoTransactionWithReceipt? _defaultInstance; 470 | 471 | @$pb.TagNumber(1) 472 | ProtoTransaction get transaction => $_getN(0); 473 | @$pb.TagNumber(1) 474 | set transaction(ProtoTransaction v) { 475 | setField(1, v); 476 | } 477 | 478 | @$pb.TagNumber(1) 479 | $core.bool hasTransaction() => $_has(0); 480 | @$pb.TagNumber(1) 481 | void clearTransaction() => clearField(1); 482 | @$pb.TagNumber(1) 483 | ProtoTransaction ensureTransaction() => $_ensure(0); 484 | 485 | @$pb.TagNumber(2) 486 | ProtoTransactionReceipt get receipt => $_getN(1); 487 | @$pb.TagNumber(2) 488 | set receipt(ProtoTransactionReceipt v) { 489 | setField(2, v); 490 | } 491 | 492 | @$pb.TagNumber(2) 493 | $core.bool hasReceipt() => $_has(1); 494 | @$pb.TagNumber(2) 495 | void clearReceipt() => clearField(2); 496 | @$pb.TagNumber(2) 497 | ProtoTransactionReceipt ensureReceipt() => $_ensure(1); 498 | } 499 | -------------------------------------------------------------------------------- /lib/src/proto/message.pbenum.dart: -------------------------------------------------------------------------------- 1 | /// 2 | // Generated code. Do not modify. 3 | // source: message.proto 4 | // 5 | 6 | // ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields 7 | -------------------------------------------------------------------------------- /lib/src/proto/message.pbjson.dart: -------------------------------------------------------------------------------- 1 | /// 2 | // Generated code. Do not modify. 3 | // source: message.proto 4 | // 5 | 6 | // ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields 7 | 8 | const ByteArray$json = const { 9 | '1': 'ByteArray', 10 | '2': const [ 11 | const {'1': 'data', '3': 1, '4': 2, '5': 12, '10': 'data'}, 12 | ], 13 | }; 14 | 15 | const ProtoTransactionCoreInfo$json = const { 16 | '1': 'ProtoTransactionCoreInfo', 17 | '2': const [ 18 | const {'1': 'version', '3': 1, '4': 1, '5': 13, '10': 'version'}, 19 | const {'1': 'nonce', '3': 2, '4': 1, '5': 4, '10': 'nonce'}, 20 | const {'1': 'toaddr', '3': 3, '4': 1, '5': 12, '10': 'toaddr'}, 21 | const { 22 | '1': 'senderpubkey', 23 | '3': 4, 24 | '4': 1, 25 | '5': 11, 26 | '6': '.ZilliqaMessage.ByteArray', 27 | '10': 'senderpubkey' 28 | }, 29 | const { 30 | '1': 'amount', 31 | '3': 5, 32 | '4': 1, 33 | '5': 11, 34 | '6': '.ZilliqaMessage.ByteArray', 35 | '10': 'amount' 36 | }, 37 | const { 38 | '1': 'gasprice', 39 | '3': 6, 40 | '4': 1, 41 | '5': 11, 42 | '6': '.ZilliqaMessage.ByteArray', 43 | '10': 'gasprice' 44 | }, 45 | const {'1': 'gaslimit', '3': 7, '4': 1, '5': 4, '10': 'gaslimit'}, 46 | const {'1': 'code', '3': 8, '4': 1, '5': 12, '10': 'code'}, 47 | const {'1': 'data', '3': 9, '4': 1, '5': 12, '10': 'data'}, 48 | ], 49 | }; 50 | 51 | const ProtoTransaction$json = const { 52 | '1': 'ProtoTransaction', 53 | '2': const [ 54 | const {'1': 'tranid', '3': 1, '4': 1, '5': 12, '10': 'tranid'}, 55 | const { 56 | '1': 'info', 57 | '3': 2, 58 | '4': 1, 59 | '5': 11, 60 | '6': '.ZilliqaMessage.ProtoTransactionCoreInfo', 61 | '10': 'info' 62 | }, 63 | const { 64 | '1': 'signature', 65 | '3': 3, 66 | '4': 1, 67 | '5': 11, 68 | '6': '.ZilliqaMessage.ByteArray', 69 | '10': 'signature' 70 | }, 71 | ], 72 | }; 73 | 74 | const ProtoTransactionReceipt$json = const { 75 | '1': 'ProtoTransactionReceipt', 76 | '2': const [ 77 | const {'1': 'receipt', '3': 1, '4': 1, '5': 12, '10': 'receipt'}, 78 | const {'1': 'cumgas', '3': 2, '4': 1, '5': 4, '10': 'cumgas'}, 79 | ], 80 | }; 81 | 82 | const ProtoTransactionWithReceipt$json = const { 83 | '1': 'ProtoTransactionWithReceipt', 84 | '2': const [ 85 | const { 86 | '1': 'transaction', 87 | '3': 1, 88 | '4': 1, 89 | '5': 11, 90 | '6': '.ZilliqaMessage.ProtoTransaction', 91 | '10': 'transaction' 92 | }, 93 | const { 94 | '1': 'receipt', 95 | '3': 2, 96 | '4': 1, 97 | '5': 11, 98 | '6': '.ZilliqaMessage.ProtoTransactionReceipt', 99 | '10': 'receipt' 100 | }, 101 | ], 102 | }; 103 | -------------------------------------------------------------------------------- /lib/src/proto/message.pbserver.dart: -------------------------------------------------------------------------------- 1 | /// 2 | // Generated code. Do not modify. 3 | // source: message.proto 4 | // 5 | 6 | // ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields 7 | 8 | export 'message.pb.dart'; 9 | -------------------------------------------------------------------------------- /lib/src/proto/message.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | package ZilliqaMessage; 4 | 5 | // ============================================================================ 6 | // Primitives 7 | // ============================================================================ 8 | 9 | message ByteArray 10 | { 11 | required bytes data = 1; 12 | } 13 | 14 | message ProtoTransactionCoreInfo 15 | { 16 | optional uint32 version = 1; 17 | optional uint64 nonce = 2; 18 | optional bytes toaddr = 3; 19 | optional ByteArray senderpubkey = 4; 20 | optional ByteArray amount = 5; 21 | optional ByteArray gasprice = 6; 22 | optional uint64 gaslimit = 7; 23 | optional bytes code = 8; 24 | optional bytes data = 9; 25 | } 26 | 27 | message ProtoTransaction 28 | { 29 | optional bytes tranid = 1; 30 | optional ProtoTransactionCoreInfo info = 2; 31 | optional ByteArray signature = 3; 32 | } 33 | 34 | message ProtoTransactionReceipt 35 | { 36 | optional bytes receipt = 1; 37 | optional uint64 cumgas = 2; 38 | } 39 | 40 | message ProtoTransactionWithReceipt 41 | { 42 | optional ProtoTransaction transaction = 1; 43 | optional ProtoTransactionReceipt receipt = 2; 44 | } 45 | -------------------------------------------------------------------------------- /lib/src/provider/Base.dart: -------------------------------------------------------------------------------- 1 | enum MiddlewareType { REQ, RES } 2 | 3 | class UseMiddleware { 4 | late dynamic use; 5 | } 6 | 7 | class Middleware { 8 | UseMiddleware request = new UseMiddleware(); 9 | UseMiddleware response = new UseMiddleware(); 10 | } 11 | 12 | abstract class ReqMiddleware { 13 | I? reqMiddleware; 14 | } 15 | 16 | abstract class ResMiddleware { 17 | O? resMiddleware; 18 | } 19 | 20 | typedef Transformer = O Function(I payload); 21 | 22 | abstract class MiddlewarePair {} 23 | 24 | class BaseProvider 25 | implements 26 | ReqMiddleware>>, 27 | ResMiddleware>> { 28 | String? url; 29 | Map>? reqMiddleware; 30 | Map>? resMiddleware; 31 | 32 | Middleware middleware = new Middleware(); 33 | 34 | BaseProvider(Map? reqMiddleware, Map? resMiddleware) { 35 | this.reqMiddleware = reqMiddleware is Map 36 | ? reqMiddleware as Map>? 37 | : {'*': []}; 38 | this.resMiddleware = resMiddleware is Map 39 | ? resMiddleware as Map>? 40 | : {'*': []}; 41 | this.middleware.request.use = (Transformer fn, {String match = '*'}) => 42 | this._pushMiddleware(fn, MiddlewareType.REQ, match); 43 | this.middleware.response.use = (Transformer fn, {String match = '*'}) => 44 | this._pushMiddleware(fn, MiddlewareType.RES, match); 45 | } 46 | 47 | void _pushMiddleware(Transformer fn, MiddlewareType type, String match) { 48 | if (type == MiddlewareType.REQ && type == MiddlewareType.RES) { 49 | throw 'Please specify the type of middleware being added'; 50 | } 51 | 52 | if (type == MiddlewareType.REQ) { 53 | var current = this.reqMiddleware![match] ?? []; 54 | List matchers = List.from(current); 55 | matchers.add(fn); 56 | this 57 | .reqMiddleware! 58 | .update(match, (found) => matchers, ifAbsent: () => matchers); 59 | } else { 60 | var current = this.resMiddleware![match] ?? []; 61 | List matchers = List.from(current); 62 | matchers.add(fn); 63 | this 64 | .resMiddleware! 65 | .update(match, (found) => matchers, ifAbsent: () => matchers); 66 | } 67 | } 68 | 69 | List> getMiddleware(String? method) { 70 | List reqFns = []; 71 | List resFns = []; 72 | 73 | for (var ent in this.reqMiddleware!.entries) { 74 | var key = ent.key; 75 | var transformers = ent.value; 76 | if (key is String && key != '*' && key == method) { 77 | reqFns.addAll(transformers); 78 | } 79 | 80 | if (key is RegExp && key.hasMatch(method!)) { 81 | reqFns.addAll(transformers); 82 | } 83 | 84 | if (key == '*') { 85 | reqFns.addAll(transformers); 86 | } 87 | } 88 | 89 | for (var ent in this.resMiddleware!.entries) { 90 | var key = ent.key; 91 | var transformers = ent.value; 92 | if (key is String && key != '*' && key == method) { 93 | resFns.addAll(transformers); 94 | } 95 | 96 | if (key is RegExp && key.hasMatch(method!)) { 97 | resFns.addAll(transformers); 98 | } 99 | 100 | if (key == '*') { 101 | resFns.addAll(transformers); 102 | } 103 | } 104 | 105 | return [reqFns, resFns]; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /lib/src/provider/Http.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:convert'; 3 | import 'package:http/http.dart'; 4 | 5 | import 'Base.dart'; 6 | import 'net.dart'; 7 | import 'Middleware.dart'; 8 | 9 | typedef MiddleWareFn = dynamic Function(List list); 10 | 11 | class HttpProvider extends BaseProvider implements RPCRequest { 12 | final int timeout = 120000; 13 | final String cache = 'no-cache'; 14 | final String mode = 'cors'; 15 | final String redirect = 'follow'; 16 | final String referrer = 'no-referrer'; 17 | 18 | Map headers = {'Content-Type': 'application/json'}; 19 | 20 | @override 21 | String? url; 22 | 23 | RPCRequestPayload? payload; 24 | RPCRequestOptions? options; 25 | 26 | HttpProvider(url, [req, res]) : super(req, res) { 27 | this.url = url; 28 | } 29 | 30 | Map buildPayload(String? method, [dynamic params]) { 31 | return { 32 | 'url': this.url, 33 | 'payload': { 34 | 'id': 1, 35 | 'jsonrpc': '2.0', 36 | 'method': method, 37 | 'params': params != null ? [params] : [] 38 | } 39 | }; 40 | } 41 | 42 | Map buildEndpointPayload(dynamic params) { 43 | return {'payload': params}; 44 | } 45 | 46 | Future send(String method, [dynamic params]) async { 47 | return this._requestFunc(method: method, params: params); 48 | } 49 | 50 | Future sendServer(String endpoint, [dynamic params]) async { 51 | return this._requestFunc(method: '', params: params, endpoint: endpoint); 52 | } 53 | 54 | Future _requestFunc( 55 | {String? method, dynamic params, String? endpoint}) async { 56 | List> middleware = this.getMiddleware(method); 57 | Transformer reqMiddleware = this.composeMiddleware(middleware.first); 58 | Transformer resMiddleware = this.composeMiddleware(middleware.last); 59 | 60 | /// compact with scilla runner if endpoint appears 61 | var req = reqMiddleware(endpoint == null 62 | ? this.buildPayload(method, params) 63 | : this.buildEndpointPayload(params)); 64 | var url = endpoint == null ? this.url! : '${this.url}${endpoint}'; 65 | var headers = this.headers; 66 | 67 | /// request to RPC 68 | RPCMiddleWare result = await performRPC(url, headers, req); 69 | return resMiddleware(result); 70 | } 71 | 72 | dynamic composeMiddleware(List middwareList) { 73 | if (middwareList.isEmpty) { 74 | return (arg) => arg; 75 | } 76 | 77 | if (middwareList.length == 1) { 78 | return middwareList.first; 79 | } 80 | 81 | if (middwareList.length > 1) { 82 | return List.from(middwareList).reduce((a, b) => (any) => a(b(any))); 83 | } 84 | } 85 | } 86 | 87 | Future performRPC( 88 | String url, Map headers, Map request) async { 89 | Client client = new Client(); 90 | 91 | var response = await client 92 | .post(Uri.parse(url), 93 | headers: headers as Map?, 94 | body: json.encode(request['payload'])) 95 | .whenComplete(client.close); 96 | 97 | Map body = json.decode(response.body); 98 | 99 | // response.statusCode; 100 | // if (response.statusCode >= 400) throw Future.error('connection error'); 101 | var newMapEntry = MapEntry('req', response.request); 102 | body.addEntries([newMapEntry]); 103 | return new RPCMiddleWare(body); 104 | } 105 | -------------------------------------------------------------------------------- /lib/src/provider/Middleware.dart: -------------------------------------------------------------------------------- 1 | import 'net.dart'; 2 | import 'dart:convert'; 3 | 4 | class ErrorMiddleware implements RPCError { 5 | RPCErrorCode? code; 6 | String? message; 7 | dynamic data; 8 | Map? raw; 9 | 10 | Map get toMap => { 11 | 'code': this.code.toString(), 12 | 'data': this.data, 13 | 'message': this.message 14 | }; 15 | 16 | ErrorMiddleware(Map error) { 17 | this.code = new RPCErrorCode(error['code']); 18 | this.data = error['data']; 19 | this.message = error['message']; 20 | this.raw = error; 21 | } 22 | 23 | @override 24 | toString() { 25 | return json.encode(this.toMap); 26 | } 27 | } 28 | 29 | class SuccessMiddleware implements RPCResult { 30 | String? resultString; 31 | Map? resultMap; 32 | List? resultList; 33 | dynamic raw; 34 | SuccessMiddleware(dynamic data) { 35 | if (data is String) { 36 | this.resultString = data; 37 | } else if (data is Map) { 38 | this.resultMap = Map.from(data); 39 | } else if (data is List) { 40 | this.resultList = List.from(data); 41 | } 42 | this.raw = data; 43 | } 44 | 45 | @override 46 | String toString() { 47 | return resultString != null 48 | ? resultString.toString() 49 | : resultMap != null 50 | ? resultMap.toString() 51 | : resultList != null 52 | ? resultList.toString() 53 | : raw != null 54 | ? raw.toString() 55 | : raw; 56 | } 57 | 58 | Map? toMap() { 59 | return resultMap; 60 | } 61 | 62 | List? toList() { 63 | return resultList; 64 | } 65 | } 66 | 67 | class RPCMiddleWare 68 | implements RPCResponseBody { 69 | final jsonrpc = '2.0'; 70 | final id = '1'; 71 | SuccessMiddleware? result; 72 | ErrorMiddleware? error; 73 | var message; 74 | var req; 75 | 76 | RPCMiddleWare.success(dynamic resultData) { 77 | result = new SuccessMiddleware(resultData); 78 | } 79 | RPCMiddleWare.error(dynamic errorData) { 80 | error = new ErrorMiddleware(errorData); 81 | } 82 | RPCMiddleWare(Map data) { 83 | if (data['result'] != null) { 84 | this.result = RPCMiddleWare.success(data['result']).result; 85 | } 86 | if (data['error'] != null) { 87 | this.error = RPCMiddleWare.error(data['error']).error; 88 | } 89 | if (data['req'] != null) { 90 | this.req = data['req']; 91 | } 92 | if (data['message'] != null) { 93 | this.message = data['message']; 94 | } 95 | } 96 | 97 | Map get raw => { 98 | 'jsonrpc': jsonrpc, 99 | 'id': id, 100 | 'result': result != null ? result!.raw : null, 101 | 'error': error != null ? error!.raw : null, 102 | 'message': message ?? error != null ? error!.message : null, 103 | 'req': req 104 | }; 105 | } 106 | -------------------------------------------------------------------------------- /lib/src/provider/index.dart: -------------------------------------------------------------------------------- 1 | library provider; 2 | 3 | export 'Base.dart'; 4 | export 'Http.dart'; 5 | export 'Middleware.dart'; 6 | export 'net.dart'; 7 | -------------------------------------------------------------------------------- /lib/src/provider/net.dart: -------------------------------------------------------------------------------- 1 | class RPCMethod { 2 | /// Network-related methods 3 | static final GetNetworkId = 'GetNetworkId'; 4 | static final GetClientVersion = 'GetClientVersion'; 5 | static final GetProtocolVersion = 'GetProtocolVersion'; 6 | 7 | /// Blockchain-related methods 8 | static final GetBalance = 'GetBalance'; 9 | static final GetBlockchainInfo = 'GetBlockchainInfo'; 10 | static final GetShardingStructure = 'GetShardingStructure'; 11 | static final GetDSBlock = 'GetDsBlock'; 12 | static final GetLatestDSBlock = 'GetLatestDsBlock'; 13 | static final GetNumDSBlocks = 'GetNumDSBlocks'; 14 | static final GetDSBlockRate = 'GetDSBlockRate'; 15 | static final DSBlockListing = 'DSBlockListing'; 16 | static final GetTxBlock = 'GetTxBlock'; 17 | static final GetLatestTxBlock = 'GetLatestTxBlock'; 18 | static final GetNumTxBlocks = 'GetNumTxBlocks'; 19 | static final GetTxBlockRate = 'GetTxBlockRate'; 20 | static final TxBlockListing = 'TxBlockListing'; 21 | static final GetNumTransactions = 'GetNumTransactions'; 22 | static final GetTransactionRate = 'GetTransactionRate'; 23 | static final GetCurrentMiniEpoch = 'GetCurrentMiniEpoch'; 24 | static final GetCurrentDSEpoch = 'GetCurrentDSEpoch'; 25 | static final GetPrevDifficulty = 'GetPrevDifficulty'; 26 | static final GetPrevDSDifficulty = 'GetPrevDSDifficulty'; 27 | 28 | /// GetBlockTransactionCount = 'GetBlockTransactionCount'; 29 | 30 | /// Transaction-related methods 31 | static final CreateTransaction = 'CreateTransaction'; 32 | static final GetTransaction = 'GetTransaction'; 33 | 34 | /// GetTransactionReceipt = 'GetTransactionReceipt'; 35 | static final GetRecentTransactions = 'GetRecentTransactions'; 36 | static final GetNumTxnsTxEpoch = 'GetNumTxnsTxEpoch'; 37 | static final GetNumTxnsDSEpoch = 'GetNumTxnsDSEpoch'; 38 | static final GetTransactionsForTxBlock = 'GetTransactionsForTxBlock'; 39 | static final GetMinimumGasPrice = 'GetMinimumGasPrice'; 40 | 41 | /// GetGasEstimate = 'GetGasEstimate'; 42 | 43 | /// Contract-related methods 44 | static final GetSmartContractCode = 'GetSmartContractCode'; 45 | static final GetSmartContractInit = 'GetSmartContractInit'; 46 | static final GetSmartContractState = 'GetSmartContractState'; 47 | static final GetContractAddressFromTransactionID = 48 | 'GetContractAddressFromTransactionID'; 49 | 50 | /// GetStorageAt = 'GetStorageAt'; 51 | 52 | Map get Mapping => { 53 | GetNetworkId: 'GetNetworkId', 54 | GetClientVersion: 'GetClientVersion', 55 | GetProtocolVersion: 'GetProtocolVersion', 56 | GetBlockchainInfo: 'GetBlockchainInfo', 57 | GetShardingStructure: 'GetShardingStructure', 58 | GetDSBlock: 'GetDsBlock', 59 | GetLatestDSBlock: 'GetLatestDsBlock', 60 | GetNumDSBlocks: 'GetNumDSBlocks', 61 | GetDSBlockRate: 'GetDSBlockRate', 62 | DSBlockListing: 'DSBlockListing', 63 | GetTxBlock: 'GetTxBlock', 64 | GetLatestTxBlock: 'GetLatestTxBlock', 65 | GetNumTxBlocks: 'GetNumTxBlocks', 66 | GetTxBlockRate: 'GetTxBlockRate', 67 | TxBlockListing: 'TxBlockListing', 68 | GetNumTransactions: 'GetNumTransactions', 69 | GetTransactionRate: 'GetTransactionRate', 70 | GetCurrentMiniEpoch: 'GetCurrentMiniEpoch', 71 | GetCurrentDSEpoch: 'GetCurrentDSEpoch', 72 | GetPrevDifficulty: 'GetPrevDifficulty', 73 | GetPrevDSDifficulty: 'GetPrevDSDifficulty', 74 | 75 | /// GetBlockTransactionCount : 'GetBlockTransactionCount', 76 | 77 | /// Transaction-related methods 78 | CreateTransaction: 'CreateTransaction', 79 | GetTransaction: 'GetTransaction', 80 | 81 | /// GetTransactionReceipt : 'GetTransactionReceipt', 82 | GetRecentTransactions: 'GetRecentTransactions', 83 | GetNumTxnsTxEpoch: 'GetNumTxnsTxEpoch', 84 | GetNumTxnsDSEpoch: 'GetNumTxnsDSEpoch', 85 | GetMinimumGasPrice: 'GetMinimumGasPrice', 86 | GetTransactionsForTxBlock: 'GetTransactionsForTxBlock', 87 | 88 | /// GetGasEstimate : 'GetGasEstimate', 89 | 90 | /// Contract-related methods 91 | GetSmartContractCode: 'GetSmartContractCode', 92 | GetSmartContractInit: 'GetSmartContractInit', 93 | GetSmartContractState: 'GetSmartContractState', 94 | GetContractAddressFromTransactionID: 95 | 'GetContractAddressFromTransactionID', 96 | 97 | /// GetStorageAt : 'GetStorageAt', 98 | 99 | /// Account-related methods 100 | GetBalance: 'GetBalance', 101 | }; 102 | } 103 | 104 | class Endpoint { 105 | /// Scilla Runner Related Method 106 | static final ScillaCheck = '/contract/check'; 107 | static final ScillaCall = '/contract/call'; 108 | 109 | Map get Mapping => { 110 | /// Scilla Runner Related Method 111 | ScillaCheck: '/contract/check', 112 | ScillaCall: '/contract/call' 113 | }; 114 | } 115 | 116 | class RPCErrorCode { 117 | // Standard JSON-RPC 2.0 errors 118 | // RPC_INVALID_REQUEST is internally mapped to HTTP_BAD_REQUEST (400). 119 | // It should not be used for application-layer errors. 120 | static final RPC_INVALID_REQUEST = -32600; 121 | // RPC_METHOD_NOT_FOUND is internally mapped to HTTP_NOT_FOUND (404). 122 | // It should not be used for application-layer errors. 123 | static final RPC_METHOD_NOT_FOUND = -32601; 124 | static final RPC_INVALID_PARAMS = -32602; 125 | // RPC_INTERNAL_ERROR should only be used for genuine errors in bitcoind 126 | // (for example datadir corruption). 127 | static final RPC_INTERNAL_ERROR = -32603; 128 | static final RPC_PARSE_ERROR = -32700; 129 | 130 | // General application defined errors 131 | // std::exception thrown in command handling 132 | static final RPC_MISC_ERROR = -1; 133 | // Unexpected type was passed as parameter 134 | static final RPC_TYPE_ERROR = -3; 135 | // Invalid address or key 136 | static final RPC_INVALID_ADDRESS_OR_KEY = -5; 137 | // Invalid; missing or duplicate parameter 138 | static final RPC_INVALID_PARAMETER = -8; 139 | // Database error 140 | static final RPC_DATABASE_ERROR = -20; 141 | // Error parsing or validating structure in raw format 142 | static final RPC_DESERIALIZATION_ERROR = -22; 143 | // General error during transaction or block submission 144 | static final RPC_VERIFY_ERROR = -25; 145 | // Transaction or block was rejected by network rules 146 | static final RPC_VERIFY_REJECTED = -26; 147 | // Client still warming up 148 | static final RPC_IN_WARMUP = -28; 149 | // RPC method is deprecated 150 | static final RPC_METHOD_DEPRECATED = -32; 151 | 152 | String? CodeString; 153 | int? CodeNumber; 154 | 155 | Map get Mapping => { 156 | RPC_INVALID_REQUEST: 'RPC_INVALID_REQUEST', 157 | RPC_METHOD_NOT_FOUND: 'RPC_METHOD_NOT_FOUND', 158 | RPC_INVALID_PARAMS: 'RPC_INVALID_PARAMS', 159 | RPC_INTERNAL_ERROR: 'RPC_INTERNAL_ERROR', 160 | RPC_PARSE_ERROR: 'RPC_PARSE_ERROR', 161 | RPC_MISC_ERROR: 'RPC_MISC_ERROR', 162 | RPC_TYPE_ERROR: 'RPC_TYPE_ERROR', 163 | RPC_INVALID_ADDRESS_OR_KEY: 'RPC_INVALID_ADDRESS_OR_KEY', 164 | RPC_INVALID_PARAMETER: 'RPC_INVALID_PARAMETER', 165 | RPC_DATABASE_ERROR: 'RPC_DATABASE_ERROR', 166 | RPC_DESERIALIZATION_ERROR: 'RPC_DESERIALIZATION_ERROR' 167 | }; 168 | RPCErrorCode(int? code) { 169 | this.CodeNumber = code; 170 | this.CodeString = this.Mapping[code]; 171 | } 172 | 173 | Error throwError() { 174 | throw this.CodeString!; 175 | } 176 | 177 | @override 178 | toString() { 179 | return this.CodeString!; 180 | } 181 | } 182 | 183 | abstract class RPCRequestPayload { 184 | static final id = 1; 185 | static final jsonrpc = '2.0'; 186 | RPCMethod? method; 187 | T? params; 188 | } 189 | 190 | abstract class RPCRequestOptions { 191 | List? headers; 192 | String? method; 193 | } 194 | 195 | abstract class RPCRequest { 196 | String? url; 197 | RPCRequestPayload? payload; 198 | RPCRequestOptions? options; 199 | } 200 | 201 | abstract class RPCResponseBase { 202 | final jsonrpc = '2.0'; 203 | final id = '1'; 204 | } 205 | 206 | abstract class RPCResponseBody extends RPCResponseBase { 207 | R? result; 208 | E? error; 209 | } 210 | 211 | abstract class RPCError { 212 | RPCErrorCode? code; 213 | String? message; 214 | dynamic data; 215 | } 216 | 217 | abstract class RPCResult { 218 | String? resultString; 219 | Map? resultMap; 220 | List? resultList; 221 | dynamic raw; 222 | } 223 | -------------------------------------------------------------------------------- /lib/src/transaction/api.dart: -------------------------------------------------------------------------------- 1 | abstract class TxParams { 2 | int? version; 3 | String? transactionID; 4 | String? toAddr; 5 | int? nonce = 0; 6 | String? pubKey; 7 | BigInt? amount; 8 | BigInt? gasPrice; 9 | int? gasLimit; 10 | String? code; 11 | String? data; 12 | Map? receipt; 13 | String? signature; 14 | } 15 | 16 | abstract class BaseTransaction extends TxParams { 17 | // Map get txParams; 18 | // Map get toPayload; 19 | // Uint8List get bytes; 20 | // String get senderAddress; 21 | } 22 | 23 | enum TxStatus { 24 | Initialised, 25 | Pending, 26 | Confirmed, 27 | Rejected, 28 | } 29 | -------------------------------------------------------------------------------- /lib/src/transaction/factory.dart: -------------------------------------------------------------------------------- 1 | import 'package:laksadart/src/core/zilliqa_module.dart'; 2 | import 'package:laksadart/src/messenger/messenger.dart'; 3 | import 'package:laksadart/src/transaction/transaction.dart'; 4 | 5 | class TransactionFactory implements ZilliqaModule { 6 | Messenger? _messenger; 7 | 8 | @override 9 | void set messenger(Messenger? messenger) => this._messenger = messenger; 10 | 11 | @override 12 | Messenger get messenger => this._messenger!; 13 | 14 | TransactionFactory(this._messenger); 15 | Transaction newTx(Map txParams) { 16 | return new Transaction(params: txParams, messenger: this.messenger); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/src/transaction/index.dart: -------------------------------------------------------------------------------- 1 | library transaction; 2 | 3 | export 'api.dart'; 4 | export 'factory.dart'; 5 | export 'transaction.dart'; 6 | export 'util.dart'; 7 | -------------------------------------------------------------------------------- /lib/src/transaction/transaction.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:laksadart/src/provider/net.dart'; 3 | import 'package:laksadart/src/messenger/messenger.dart'; 4 | import 'package:laksadart/src/account/address.dart'; 5 | import 'package:laksadart/src/crypto/index.dart'; 6 | import 'util.dart'; 7 | import 'api.dart'; 8 | 9 | class TransactionSent { 10 | Transaction transaction; 11 | Map result; 12 | TransactionSent(this.transaction, this.result); 13 | } 14 | 15 | class Transaction implements BaseTransaction { 16 | int? version = 0; 17 | String? transactionID; 18 | String? toAddr; 19 | int? nonce = 0; 20 | String? pubKey; 21 | BigInt? amount; 22 | BigInt? gasPrice; 23 | int? gasLimit; 24 | String? code; 25 | String? data; 26 | Map? receipt; 27 | String? signature; 28 | String get senderAddress => this._senderAddress(); 29 | // messenger 30 | Messenger? messenger; 31 | bool toDS = false; 32 | 33 | String getReceiptInfo(String blockExplorer) => 34 | "Result: ${receipt!['success'] ? "Success" : "Failure"}\nhttps://viewblock.io/zilliqa/tx/0x${this.transactionID}?network=${blockExplorer}"; 35 | 36 | // getter txParmas 37 | Map get txParams => { 38 | 'version': this.version, 39 | 'toAddr': ZilAddress.toValidAddress(this.toAddr!), 40 | 'nonce': this.nonce, 41 | 'pubKey': this.pubKey, 42 | 'amount': this.amount, 43 | 'gasPrice': this.gasPrice, 44 | 'gasLimit': this.gasLimit, 45 | 'code': this.code, 46 | 'data': this.data, 47 | 'signature': this.signature, 48 | 'receipt': this.receipt, 49 | }; 50 | 51 | // getter toPayload 52 | Map get toPayload => { 53 | 'version': this.version, 54 | 'toAddr': ZilAddress.toValidAddress(this.toAddr!), 55 | 'nonce': this.nonce, 56 | 'pubKey': this.pubKey, 57 | 'amount': this.amount.toString(), 58 | 'gasPrice': this.gasPrice.toString(), 59 | 'gasLimit': this.gasLimit.toString(), 60 | 'code': this.code, 61 | 'data': this.data, 62 | 'signature': this.signature, 63 | 'priority': this.toDS 64 | }; 65 | 66 | TxStatus? status; 67 | 68 | String _senderAddress() { 69 | if (this.pubKey != null) { 70 | return '0' * 40; 71 | } 72 | return getAddressFromPublicKey(this.pubKey!); 73 | } 74 | 75 | Transaction( 76 | {required Map params, 77 | Messenger? messenger, 78 | TxStatus status = TxStatus.Initialised, 79 | bool toDS = false}) { 80 | // params 81 | this.version = params['version']; 82 | this.transactionID = params['TranID']; 83 | this.toAddr = ZilAddress.toValidAddress(params['toAddr']); 84 | this.nonce = params['nonce']; 85 | this.pubKey = params['pubKey']; 86 | this.amount = params['amount']; 87 | this.code = params['code'] ?? ''; 88 | this.data = params['data'] ?? ''; 89 | this.signature = params['signature']; 90 | this.gasPrice = params['gasPrice']; 91 | this.gasLimit = params['gasLimit']; 92 | this.receipt = params['receipt']; 93 | // // status 94 | this.status = status; 95 | this.messenger = messenger; 96 | this.toDS = toDS; 97 | } 98 | 99 | bool isPending() { 100 | return this.status == TxStatus.Pending; 101 | } 102 | 103 | bool isInitialised() { 104 | return this.status == TxStatus.Initialised; 105 | } 106 | 107 | bool isConfirmed() { 108 | return this.status == TxStatus.Confirmed; 109 | } 110 | 111 | bool isRejected() { 112 | return this.status == TxStatus.Rejected; 113 | } 114 | 115 | static confirmTxn(Map params, Messenger messenger) { 116 | return new Transaction( 117 | params: params, messenger: messenger, status: TxStatus.Confirmed); 118 | } 119 | 120 | static reject(Map params, Messenger messenger) { 121 | return new Transaction( 122 | params: params, messenger: messenger, status: TxStatus.Rejected); 123 | } 124 | 125 | void setProvider(Messenger messenger) { 126 | this.messenger = messenger; 127 | } 128 | 129 | Transaction setStatus(TxStatus status) { 130 | this.status = status; 131 | return this; 132 | } 133 | 134 | void setParams(Map params) { 135 | this.version = params['version']; 136 | this.transactionID = params['TranID']; 137 | this.toAddr = ZilAddress.toValidAddress(params['toAddr']); 138 | this.nonce = params['nonce']; 139 | this.pubKey = params['pubKey']; 140 | this.amount = params['amount']; 141 | this.code = params['code']; 142 | this.data = params['data']; 143 | this.signature = params['signature']; 144 | this.gasPrice = params['gasPrice']; 145 | this.gasLimit = params['gasLimit']; 146 | this.receipt = params['receipt']; 147 | } 148 | 149 | Transaction map(Function mapFn) { 150 | Map newParams = mapFn(this.txParams); 151 | 152 | this.setParams(newParams); 153 | return this; 154 | } 155 | 156 | Future sendTransaction() async { 157 | try { 158 | if (this.signature == null) { 159 | throw 'The Transaction has not been signed'; 160 | } 161 | var res = await this 162 | .messenger! 163 | .send(RPCMethod.CreateTransaction, this.toPayload); 164 | 165 | if (res.result != null) { 166 | var result = res.result!.toMap()!; 167 | var TranID = result['TranID']; 168 | if (TranID == null) { 169 | throw 'Transaction fail'; 170 | } else { 171 | this.transactionID = TranID; 172 | this.status = TxStatus.Pending; 173 | return new TransactionSent(this, result); 174 | } 175 | } else if (res.error != null) { 176 | throw res.error!.message!; 177 | } else 178 | return null; 179 | } catch (error) { 180 | rethrow; 181 | } 182 | } 183 | 184 | Future trackTx(String? txHash) async { 185 | var res = await this.messenger!.send(RPCMethod.GetTransaction, txHash); 186 | if (res.error != null) { 187 | return false; 188 | } 189 | 190 | this.transactionID = res.result!.resultMap!['ID']; 191 | this.receipt = res.result!.resultMap!['receipt']; 192 | this.status = this.receipt != null && this.receipt!['success'] 193 | ? TxStatus.Confirmed 194 | : TxStatus.Rejected; 195 | 196 | return true; 197 | } 198 | 199 | Future confirm( 200 | {String? txHash, int? maxAttempts = 20, int? interval = 1000}) async { 201 | this.status = TxStatus.Pending; 202 | int attempt = 0; 203 | do { 204 | try { 205 | if (await this.trackTx(txHash)) { 206 | return this; 207 | } 208 | } catch (err) { 209 | this.status = TxStatus.Rejected; 210 | rethrow; 211 | } 212 | if (attempt < maxAttempts! - 1) { 213 | sleep(ms: interval! * attempt, callback: () => attempt += 1); 214 | } else 215 | break; 216 | } while (attempt < maxAttempts); 217 | 218 | this.status = TxStatus.Rejected; 219 | throw 'The transaction is still not confirmed after ${maxAttempts} attemps.'; 220 | } 221 | 222 | int getVersion() { 223 | var CHAIN_ID_BIT = 2 << 16; 224 | var b = this.version!; 225 | return CHAIN_ID_BIT + b; 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /lib/src/transaction/util.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | const mil = const Duration(milliseconds: 1); 4 | Timer sleep({required int ms, Function? callback}) { 5 | var duration = mil * ms; 6 | return new Timer(duration, () => callback); 7 | } 8 | -------------------------------------------------------------------------------- /lib/src/utils/common.dart: -------------------------------------------------------------------------------- 1 | import './numbers.dart' as numbers; 2 | 3 | String toHex(dynamic msg) { 4 | var res = ''; 5 | for (var i = 0; i < msg.length; i++) { 6 | res += zero2(numbers.toHex(msg[i])); 7 | } 8 | return res; 9 | } 10 | 11 | String zero2(word) { 12 | if (word.length == 1) 13 | return '0' + word; 14 | else 15 | return word; 16 | } 17 | 18 | bool isSurrogatePair(String msg, i) { 19 | if ((msg.codeUnitAt(i) & 0xFC00) != 0xD800) { 20 | return false; 21 | } 22 | if (i < 0 || i + 1 >= msg.length) { 23 | return false; 24 | } 25 | return (msg.codeUnitAt(i + 1) & 0xFC00) == 0xDC00; 26 | } 27 | 28 | List toArray(String? msg, [String? enc]) { 29 | if (enc == 'hex') { 30 | List hexRes = []; 31 | msg = msg!.replaceAll(new RegExp("[^a-z0-9]"), ''); 32 | if (msg.length % 2 != 0) msg = '0' + msg; 33 | for (var i = 0; i < msg.length; i += 2) { 34 | var cul = msg[i] + msg[i + 1]; 35 | var result = int.parse(cul, radix: 16); 36 | hexRes.add(result); 37 | } 38 | return hexRes; 39 | } else { 40 | List noHexRes = []; 41 | for (var i = 0; i < msg!.length; i++) { 42 | var c = msg.codeUnitAt(i); 43 | var hi = c >> 8; 44 | var lo = c & 0xff; 45 | if (hi > 0) { 46 | noHexRes.add(hi); 47 | noHexRes.add(lo); 48 | } else { 49 | noHexRes.add(lo); 50 | } 51 | } 52 | 53 | return noHexRes; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/src/utils/index.dart: -------------------------------------------------------------------------------- 1 | library utils; 2 | 3 | export 'numbers.dart'; 4 | export 'transaction.dart'; 5 | export 'unit.dart'; 6 | export 'validators.dart'; 7 | -------------------------------------------------------------------------------- /lib/src/utils/network.dart: -------------------------------------------------------------------------------- 1 | //https://dev.zilliqa.com/docs/apis/api-introduction/ 2 | import 'package:laksadart/src/data/network/network_info.dart'; 3 | 4 | var zilliqaNetworks = { 5 | "mainnet": NetworkInfo( 6 | chainID: 1, 7 | networkID: "Zilliqa Mainnet", 8 | nodeProviderUrl: "https://api.zilliqa.com/", 9 | blockExplorerUrl: "https://viewblock.io/zilliqa", 10 | blockExplorerNetwork: "mainnet"), 11 | "dev": NetworkInfo( 12 | chainID: 333, 13 | networkID: "Developer Testnet", 14 | nodeProviderUrl: "https://dev-api.zilliqa.com/", 15 | blockExplorerUrl: "https://viewblock.io/zilliqa?network=testnet", 16 | blockExplorerNetwork: "testnet"), 17 | "local": NetworkInfo( 18 | chainID: 2, 19 | networkID: "Local Testnet", 20 | nodeProviderUrl: "http://localhost:4201/"), 21 | "isolated": NetworkInfo( 22 | chainID: 63, 23 | networkID: "Isolated Server", 24 | nodeProviderUrl: "https://zilliqa-isolated-server.zilliqa.com/"), 25 | }; 26 | -------------------------------------------------------------------------------- /lib/src/utils/numbers.dart: -------------------------------------------------------------------------------- 1 | import "dart:typed_data"; 2 | import 'package:convert/convert.dart'; 3 | import 'package:pointycastle/src/utils.dart' as p_utils; 4 | 5 | /// If present, removes the 0x from the start of a hex-string. 6 | String strip0x(String hex) { 7 | if (hex.startsWith('0x', 0)) return hex.substring(2); 8 | return hex; 9 | } 10 | 11 | /// Converts the [number], which can either be a dart [int] or a [BigInt], 12 | /// into a hexadecimal representation. The number needs to be positive or zero. 13 | /// 14 | /// When [pad] is set to true, this method will prefix a zero so that the result 15 | /// will have an even length. Further, if [forcePadLen] is not null and the 16 | /// result has a length smaller than [forcePadLen], the rest will be left-padded 17 | /// with zeroes. Note that [forcePadLen] refers to the string length, meaning 18 | /// that one byte has a length of 2. When [include0x] is set to true, the 19 | /// output wil have "0x" prepended to it after any padding is done. 20 | String toHex(dynamic number, 21 | {bool pad = false, bool include0x = false, int? forcePadLen}) { 22 | String toHexSimple() { 23 | if (number is int) 24 | return number.toRadixString(16); 25 | else if (number is BigInt) 26 | return number.toRadixString(16); 27 | else 28 | throw new TypeError(); 29 | } 30 | 31 | var hexString = toHexSimple(); 32 | if (pad && !hexString.length.isEven) hexString = "0$hexString"; 33 | if (forcePadLen != null) hexString = hexString.padLeft(forcePadLen, "0"); 34 | if (include0x) hexString = "0x$hexString"; 35 | 36 | return hexString; 37 | } 38 | 39 | /// Converts the [bytes] given as a list of integers into a hexadecimal 40 | /// representation. 41 | /// 42 | /// If any of the bytes is outside of the range [0, 256], the method will throw. 43 | /// The outcome of this function will prefix a 0 if it would otherwise not be 44 | /// of even length. If [include0x] is set, it will prefix "0x" to the hexadecimal 45 | /// representation. 46 | String bytesToHex(List bytes, {bool include0x = false}) { 47 | return (include0x ? "0x" : "") + hex.encode(bytes); 48 | } 49 | 50 | /// Converts the given number, either a [int] or a [BigInt] to a list of 51 | /// bytes representing the same value. 52 | List numberToBytes(dynamic number) { 53 | if (number is BigInt) return p_utils.encodeBigInt(number); 54 | 55 | var hexString = toHex(number, pad: true); 56 | return hex.decode(hexString); 57 | } 58 | 59 | /// Converts the hexadecimal string, which can be prefixed with 0x, to a byte 60 | /// sequence. 61 | List hexToBytes(String hexStr) { 62 | return hex.decode(strip0x(hexStr)); 63 | } 64 | 65 | ///Converts the bytes from that list (big endian) to a BigInt. 66 | // BigInt bytesToInt(List bytes) => p_utils.decodeBigInt(bytes); 67 | BigInt bytesToInt(List bytes) => p_utils.decodeBigIntWithSign(1, bytes); 68 | 69 | // List intToBytes(BigInt number) => p_utils.encodeBigInt(number); 70 | 71 | /// big int to bytes 72 | List intToBytes(BigInt? number, {int? length}) { 73 | Uint8List bigIntList = p_utils.encodeBigInt(number); 74 | if (length != null && length > bigIntList.length) { 75 | var newList = new Int8List(length); 76 | newList.setRange(length - bigIntList.length, length, bigIntList); 77 | return newList; 78 | } else if (length == null) { 79 | return bigIntList; 80 | } else { 81 | throw 'length is to short, should be >= ${bigIntList.length}'; 82 | } 83 | } 84 | 85 | ///Takes the hexadecimal input and creates a BigInt. 86 | BigInt hexToInt(String hex) { 87 | return BigInt.parse(strip0x(hex), radix: 16); 88 | } 89 | 90 | int pack(int CHAIN_ID, int rawVersion) { 91 | return (CHAIN_ID << 16) + rawVersion; 92 | } 93 | 94 | int unPack(int versioned, int CHAIN_ID) { 95 | return versioned - (CHAIN_ID << 16); 96 | } 97 | 98 | List numberToHexArray(int number, int size) { 99 | String hexVal = number.toRadixString(16); 100 | List hexRep = List.filled(hexVal.length, '0'); 101 | List hex = List.filled(size, '0'); 102 | for (int i = 0; i < hexVal.length; i++) { 103 | hexRep[i] = hexVal.substring(i, i + 1); 104 | } 105 | hex.setRange(size - hexVal.length, size, hexRep); 106 | return hex; 107 | } 108 | -------------------------------------------------------------------------------- /lib/src/utils/transaction.dart: -------------------------------------------------------------------------------- 1 | import 'dart:core'; 2 | import 'dart:typed_data'; 3 | import 'package:fixnum/fixnum.dart'; 4 | import 'package:laksadart/src/proto/message.pb.dart' as zMessage; 5 | import './numbers.dart' as numbers; 6 | import './common.dart'; 7 | 8 | Uint8List encodeTransactionProto(Map tx) { 9 | var txnBuffer = zMessage.ProtoTransactionCoreInfo.create(); 10 | txnBuffer.version = tx['version']; 11 | txnBuffer.nonce = tx['nonce'] is int ? Int64(tx['nonce']) : Int64(0); 12 | txnBuffer.toaddr = 13 | numbers.hexToBytes(tx['toAddr'].replaceAll('0x', '').toLowerCase()); 14 | txnBuffer.senderpubkey = zMessage.ByteArray.create(); 15 | txnBuffer.senderpubkey.data = 16 | numbers.hexToBytes(tx['pubKey'] is String ? tx['pubKey'] : '00'); 17 | 18 | txnBuffer.amount = zMessage.ByteArray.create(); 19 | txnBuffer.amount.data = numbers.intToBytes(tx['amount'], length: 16); 20 | txnBuffer.gasprice = zMessage.ByteArray.create(); 21 | txnBuffer.gasprice.data = numbers.intToBytes(tx['gasPrice'], length: 16); 22 | txnBuffer.gaslimit = tx['gasLimit'] is int ? Int64(tx['gasLimit']) : Int64(0); 23 | 24 | if (null != tx['code'] && 25 | tx['code'] is String && 26 | tx['code'].toString().isNotEmpty) { 27 | txnBuffer.code = toArray(tx['code']); 28 | } 29 | if (null != tx['data'] && 30 | tx['data'] is String && 31 | tx['data'].toString().isNotEmpty) { 32 | txnBuffer.data = toArray(tx['data']); 33 | } 34 | return txnBuffer.writeToBuffer(); 35 | } 36 | -------------------------------------------------------------------------------- /lib/src/utils/unit.dart: -------------------------------------------------------------------------------- 1 | enum Units { Zil, Li, Qa } 2 | 3 | Map unitMap = { 4 | Units.Qa: '1', 5 | Units.Li: '1000000', 6 | Units.Zil: '1000000000000' 7 | }; 8 | 9 | String fromQaFunc(BigInt? qa, Units unit, {pad = false}) { 10 | if (unit.toString() == 'qa') { 11 | return qa.toString(); 12 | } 13 | String? baseStr = unitMap[unit]; 14 | 15 | if (baseStr == null) { 16 | throw 'No unit of type ${unit} exists.'; 17 | } 18 | 19 | BigInt base = BigInt.parse(baseStr); 20 | int baseNumDecimals = baseStr.length - 1; 21 | 22 | String fraction = ((qa!.abs()) % base).toString(); 23 | 24 | while (fraction.length < baseNumDecimals) { 25 | fraction = '0${fraction}'; 26 | } 27 | var reg = new RegExp(r"/^([0-9]*[1-9]|0)(0*)/"); 28 | // print(reg.allMatches(fraction)); 29 | if (pad == false) { 30 | fraction = fraction.splitMapJoin(reg); 31 | } 32 | 33 | String whole = (qa ~/ base).toString(); 34 | 35 | return fraction == '0' ? '${whole}' : '${whole}.${fraction}'; 36 | } 37 | 38 | BigInt toQaFunc(String input, Units unit) { 39 | String inputStr = num.parse(input).toString(); 40 | String? baseStr = unitMap[unit]; 41 | 42 | if (baseStr == null) { 43 | throw 'No unit of type ${unit} exists.'; 44 | } 45 | 46 | int baseNumDecimals = baseStr.length - 1; 47 | 48 | BigInt base = BigInt.parse(baseStr); 49 | 50 | // Is it negative? 51 | bool isNegative = inputStr.substring(0, 1) == '-'; 52 | if (isNegative) { 53 | inputStr = inputStr.substring(1); 54 | } 55 | 56 | if (inputStr == '.') { 57 | throw 'type error: Cannot convert ${inputStr} to Qa.'; 58 | } 59 | 60 | // Split it into a whole and fractional part 61 | List comps = inputStr.split('.'); // eslint-disable-line 62 | 63 | if (comps.length > 2) { 64 | throw 'too long: Cannot convert ${inputStr} to Qa.'; 65 | } 66 | String? whole; 67 | try { 68 | whole = comps.first; 69 | } on StateError { 70 | whole = '0'; 71 | } 72 | String? fraction = comps.length == 2 ? comps.last : null; 73 | 74 | if (fraction == null) { 75 | fraction = '0'; 76 | } 77 | if (fraction.length > baseNumDecimals) { 78 | throw 'fraction is too long :Cannot convert ${inputStr} to Qa.'; 79 | } 80 | 81 | while (fraction!.length < baseNumDecimals) { 82 | fraction += '0'; 83 | } 84 | 85 | BigInt wholeBN = BigInt.parse(whole); 86 | BigInt fractionBN = BigInt.parse(fraction); 87 | BigInt wei = wholeBN * base + fractionBN; 88 | 89 | if (isNegative) { 90 | wei = -(wei); 91 | } 92 | 93 | return BigInt.parse(wei.toString()); 94 | } 95 | 96 | class Unit { 97 | late String unit; 98 | BigInt? qa; 99 | static from(dynamic str) { 100 | return new Unit(str); 101 | } 102 | 103 | static Zil(dynamic str) { 104 | return new Unit(str).asZil(); 105 | } 106 | 107 | static Li(dynamic str) { 108 | return new Unit(str).asLi(); 109 | } 110 | 111 | static Qa(dynamic str) { 112 | return new Unit(str).asQa(); 113 | } 114 | 115 | Unit(dynamic str) { 116 | this.unit = str is String 117 | ? num.parse(str).toString() 118 | : str is int 119 | ? str.toString() 120 | : str is BigInt 121 | ? str.toString() 122 | : ''; 123 | } 124 | 125 | asZil() { 126 | this.qa = toQaFunc(this.unit, Units.Zil); 127 | return this; 128 | } 129 | 130 | asLi() { 131 | this.qa = toQaFunc(this.unit, Units.Li); 132 | return this; 133 | } 134 | 135 | asQa() { 136 | this.qa = BigInt.parse(this.unit); 137 | return this; 138 | } 139 | 140 | toQa() { 141 | return this.qa; 142 | } 143 | 144 | toLi() { 145 | return fromQaFunc(this.qa, Units.Li); 146 | } 147 | 148 | toZil() { 149 | return fromQaFunc(this.qa, Units.Zil); 150 | } 151 | 152 | toQaString() { 153 | return this.qa.toString(); 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /lib/src/utils/validators.dart: -------------------------------------------------------------------------------- 1 | import 'package:validators/validators.dart' as validators; 2 | import 'package:laksadart/src/crypto/checksum.dart'; 3 | 4 | bool isUrl(String? url) { 5 | return validators.isURL(url); 6 | } 7 | 8 | bool isByteString(String byStr, {int? length}) { 9 | var str = byStr.startsWith(new RegExp(r'0x', caseSensitive: false)) 10 | ? byStr.substring(2) 11 | : byStr; 12 | return validators.matches(str, '^[0-9a-fA-F]{${length}}') && 13 | validators.isLength(str, length!, length); 14 | } 15 | 16 | bool isAddress(String str) { 17 | return isByteString(str, length: 40); 18 | } 19 | 20 | bool isPrivateKey(String str) { 21 | return isByteString(str, length: 64); 22 | } 23 | 24 | bool isPublicKey(String str) { 25 | return isByteString(str, length: 66); 26 | } 27 | 28 | bool isSignature(String str) { 29 | return isByteString(str, length: 128); 30 | } 31 | 32 | bool isValidChecksumAddress(String str) { 33 | return (isAddress(str.replaceAll('0x', '')) && toChecksum(str) == str); 34 | } 35 | 36 | bool isBech32(String str) { 37 | return validators.matches(str, 'zil1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]{38}'); 38 | } 39 | -------------------------------------------------------------------------------- /lib/src/zilliqa.dart: -------------------------------------------------------------------------------- 1 | import 'package:laksadart/src/provider/index.dart'; 2 | import 'package:laksadart/src/messenger/index.dart'; 3 | import 'package:laksadart/src/account/index.dart'; 4 | import 'package:laksadart/src/transaction/index.dart'; 5 | import 'package:laksadart/src/contract/index.dart'; 6 | import 'package:laksadart/src/utils/validators.dart' as validators; 7 | 8 | import 'data/network/network_info.dart'; 9 | 10 | class Zilliqa { 11 | NetworkInfo? network; 12 | String? nodeUrl; 13 | HttpProvider? nodeProvider; 14 | Messenger? _messenger; 15 | Blockchain? _blockchain; 16 | Wallet? _wallet; 17 | Contracts? _contracts; 18 | TransactionFactory? _transactions; 19 | 20 | Zilliqa({NetworkInfo? network, String? nodeUrl, HttpProvider? nodeProvider}) { 21 | if (network != null) { 22 | this.nodeUrl = network.nodeProviderUrl; 23 | this.network = network; 24 | } else if (nodeUrl != null) { 25 | this.nodeUrl = nodeUrl; 26 | } else if (nodeProvider != null) { 27 | this.nodeUrl = nodeProvider.url; 28 | } else { 29 | throw Exception("Please provide network provider details"); 30 | } 31 | this.nodeProvider = 32 | nodeProvider != null ? nodeProvider : HttpProvider(this.nodeUrl); 33 | this._messenger = Messenger(nodeProvider: this.nodeProvider); 34 | this._wallet = Wallet(this._messenger); 35 | this._blockchain = Blockchain(this._messenger); 36 | this._contracts = 37 | Contracts(messenger: this._messenger, wallet: this._wallet); 38 | this._transactions = TransactionFactory(this._messenger); 39 | } 40 | 41 | bool isValidNodeUrl(String? nodeUrl) => 42 | nodeUrl != null && validators.isUrl(this.nodeUrl); 43 | 44 | TransactionFactory get transactions => this._transactions!; 45 | 46 | Blockchain get blockchain => this._blockchain!; 47 | 48 | Contracts get contracts => this._contracts!; 49 | 50 | Messenger get messenger => this._messenger!; 51 | 52 | Wallet get wallet => this._wallet!; 53 | } 54 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: laksadart 2 | description: Dart library for Zilliqa's Blockchain, including Schnorr, wallet/account/keys manager, RPCMethod, Transactions and Contracts deploy/call. 3 | version: 0.2.1+1 4 | homepage: 'https://github.com/firestack-lab/laksaDart' 5 | 6 | environment: 7 | sdk: '>=2.12.0 <3.0.0' 8 | 9 | dependencies: 10 | bip32: ^2.0.0 11 | bip39: ^1.0.6 12 | bech32: ^0.2.0 13 | collection: ^1.15.0 14 | convert: ^3.0.0 15 | crypto: ^3.0.1 16 | fixnum: ^1.0.0 17 | http: ^0.13.3 18 | meta: ^1.3.0 19 | pointycastle: ^3.0.1 20 | protobuf: ^2.0.0 21 | tuple: ^2.0.0 22 | uuid: ^3.0.4 23 | validators: ^3.0.0 24 | freezed_annotation: ^0.14.2 25 | source_helper: ^1.2.1 26 | 27 | dev_dependencies: 28 | build_runner: ^2.0.4 29 | json_serializable: ^5.0.0 30 | freezed: ^0.14.2 31 | test: ^1.17.5 32 | -------------------------------------------------------------------------------- /scripts/commit.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | cd .. 3 | git add . && git commit && git push -------------------------------------------------------------------------------- /scripts/pub.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | ./commit.sh 3 | 4 | cd .. 5 | pub publish --dry-run -------------------------------------------------------------------------------- /test/contract.dart: -------------------------------------------------------------------------------- 1 | @TestOn("vm") 2 | 3 | import 'dart:convert'; 4 | import 'dart:io'; 5 | import "package:test/test.dart"; 6 | import "package:laksadart/src/contract/factory.dart"; 7 | import 'package:laksadart/src/contract/abi.dart'; 8 | import 'package:laksadart/src/zilliqa.dart'; 9 | 10 | void main() { 11 | test("Test Get Contract ABIs", () async { 12 | File contract = new File('contracts/helloworldversion.txt'); 13 | await contract.readAsString().then((contractString) async { 14 | Zilliqa laksa = new Zilliqa(nodeUrl: 'https://dev-api.zilliqa.com'); 15 | 16 | var result = await laksa.blockchain.checkCode(code: contractString); 17 | 18 | if (result.result.toString() != 'error' && result.message != null) { 19 | var abi = json.decode(result.message)['contract_info']; 20 | var abiObject = new ABI(abi); 21 | expect(abiObject.vname, equals('HelloWorld')); 22 | expect(abiObject.params.toString(), 23 | equals('[{vname: owner, type: ByStr20}]')); 24 | expect(abiObject.fields.toString(), 25 | equals('[{vname: welcome_msg, type: String, depth: 0}]')); 26 | expect( 27 | abiObject.transitions.toString(), 28 | equals( 29 | '[{vname: setHello, params: [{vname: msg, type: String}]}, {vname: getHello, params: []}]')); 30 | expect( 31 | abiObject.events.toString(), 32 | equals( 33 | '[{vname: getHello(), params: [{vname: msg, type: String}]}, {vname: setHello(), params: [{vname: code, type: Int32}]}]')); 34 | } 35 | }); 36 | }); 37 | test('Test call to scilla-runner', () async { 38 | File contract = new File('contracts/helloworldversion.txt'); 39 | await contract.readAsString().then((contractString) async { 40 | Zilliqa laksa = new Zilliqa(nodeUrl: 'https://dev-api.zilliqa.com'); 41 | var init = [ 42 | {'vname': "_scilla_version", 'type': "Uint32", 'value': "0"}, 43 | { 44 | 'value': '0x9bfec715a6bd658fcb62b0f8cc9bfa2ade71434a', 45 | 'vname': 'owner', 46 | 'type': 'ByStr20' 47 | } 48 | ]; 49 | var contracts = 50 | new Contracts(messenger: laksa.messenger, wallet: laksa.wallet); 51 | var testResult = 52 | await contracts.testContract(code: contractString, init: init); 53 | 54 | expect(testResult, equals(true)); 55 | }); 56 | }); 57 | /* 58 | test("Test deploy", () async { 59 | File contract = new File('./contracts/helloworld.txt'); 60 | await contract.readAsString().then((contractString) async { 61 | Laksa laksa = new Laksa(nodeUrl: 'https://api.zilliqa.com'); 62 | laksa.setScillaProvider('https://scilla-runner.zilliqa.com'); 63 | var init = [ 64 | { 65 | 'value': '0x9bfec715a6bd658fcb62b0f8cc9bfa2ade71434a', 66 | 'vname': 'owner', 67 | 'type': 'ByStr20' 68 | } 69 | ]; 70 | 71 | laksa.wallet!.add( 72 | 'e19d05c5452598e24caad4a0d85a49146f7be089515c905ae6a19e8a578a6930'); 73 | 74 | var newContract = new Contracts( 75 | params: {'code': contractString, 'init': init}, 76 | messenger: laksa.messenger, 77 | wallet: laksa.wallet); 78 | 79 | var deployed = await newContract.deploy( 80 | gasLimit: 2500000000, gasPrice: BigInt.from(1000000000)); 81 | print(deployed.ContractAddress); 82 | }); 83 | }); */ 84 | } 85 | -------------------------------------------------------------------------------- /test/fixtures/keystores.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "keystore": { 4 | "address": "9aecb3d2e90b416e2be8280f678139868be55486", 5 | "crypto": { 6 | "cipher": "aes-128-ctr", 7 | "cipherparams": { "iv": "6975d2ffa4a523492040d744b6ad14b9" }, 8 | "ciphertext": "7a3196eb5e0b001b36003018c3088851d2278d30020da24e07eac6d19dfe3c87", 9 | "kdf": "scrypt", 10 | "kdfparams": { 11 | "salt": "cac2777a529b7b819ab3ebcbcebf6dd64a8c0c70ed4053b23a3fa30aa2f0ced2", 12 | "n": 8192, 13 | "c": 262144, 14 | "r": 8, 15 | "p": 1, 16 | "dklen": 32 17 | }, 18 | "mac": "53614618e3285987477b07db00c5460eff13bce3211e4c424c5387f0407e0914" 19 | }, 20 | "id": "31616335-6236-4166-a663-336231373238", 21 | "version": 3 22 | }, 23 | "privateKey": "cb71f8172af45efa7350c72b55718cb075004e9fb361a7d3ef13f8d5c3baa482", 24 | "passphrase": "277281fdeea0e597" 25 | }, 26 | { 27 | "keystore": { 28 | "address": "9f40fbff9eda6fcd560e391c06386bc9606aedaa", 29 | "crypto": { 30 | "cipher": "aes-128-ctr", 31 | "cipherparams": { "iv": "9016298cac601a6a56b0f523ab3e005f" }, 32 | "ciphertext": "a004cfa3699327acf87961f134e6dd070adb5fde6bfce0d75c2e90753f590126", 33 | "kdf": "scrypt", 34 | "kdfparams": { 35 | "salt": "3c51b6c635ab667d89001218ac58006d6032b664fa6f66bbb3056dd8cde95477", 36 | "n": 8192, 37 | "c": 262144, 38 | "r": 8, 39 | "p": 1, 40 | "dklen": 32 41 | }, 42 | "mac": "24ce7ecf91c8cdf5f4e277e71795add16823efc0a7568a5a9fa6eefdbebcd5c7" 43 | }, 44 | "id": "30656331-3338-4363-b166-616636373762", 45 | "version": 3 46 | }, 47 | "privateKey": "78b5a225adf40f32658d9956071afcaf252db568387cb97bbfe5c507276f8d67", 48 | "passphrase": "68568e8e9d5e1151" 49 | }, 50 | { 51 | "keystore": { 52 | "address": "e1f7eb9c92dc27748c3e673c35fe44e5bacb33f7", 53 | "crypto": { 54 | "cipher": "aes-128-ctr", 55 | "cipherparams": { "iv": "f7bdd5ec1340b52237849f2d55c6c56a" }, 56 | "ciphertext": "13366a679b7306f0cb23958b36a3ade67800d6e613aa900f778a78677a9c4a1d", 57 | "kdf": "scrypt", 58 | "kdfparams": { 59 | "salt": "21d31d58ea5f0851552a36f86997bc451cc5ed86912b2783053c03bc1b76042b", 60 | "n": 8192, 61 | "c": 262144, 62 | "r": 8, 63 | "p": 1, 64 | "dklen": 32 65 | }, 66 | "mac": "a1bb48a05e2692577a044e85f0b490a6a7fcbbc2af3ec85ee0c3dd05f6fd92e3" 67 | }, 68 | "id": "38363431-3030-4830-a431-303564373665", 69 | "version": 3 70 | }, 71 | "privateKey": "ce99c43a81b66c84c09a8d9c7b00af172d7c3003ee5d0dca080709cd843498fb", 72 | "passphrase": "b0cb6ce5cd2c5802" 73 | }, 74 | { 75 | "keystore": { 76 | "address": "b79ce2630aa1255c47af51e102bae46772090f79", 77 | "crypto": { 78 | "cipher": "aes-128-ctr", 79 | "cipherparams": { "iv": "3f3e0f781aa9616d1a1aa3ddd30a2050" }, 80 | "ciphertext": "e482889816f10320fa3dd30fa6940e1e159b5f6e10958335c96bc134ad62f1e4", 81 | "kdf": "scrypt", 82 | "kdfparams": { 83 | "salt": "13ddf6da43f228ad0a438e8d0dec687687a9b4dca3667fbbf8c90da8fc69a8f7", 84 | "n": 8192, 85 | "c": 262144, 86 | "r": 8, 87 | "p": 1, 88 | "dklen": 32 89 | }, 90 | "mac": "562101c9b7011805e139a695d519b1d4d26cf73590618cbbbcc40d93583ed311" 91 | }, 92 | "id": "63636532-3438-4134-b334-633930383030", 93 | "version": 3 94 | }, 95 | "privateKey": "f4d47000b2baddcead198f51e6de89a3035af87cb25d65f5005bfa1a0bff497c", 96 | "passphrase": "38222f97363835bf" 97 | }, 98 | { 99 | "keystore": { 100 | "address": "7675bcc39a3b4290a5059d4ec925b5bad1ce7d7e", 101 | "crypto": { 102 | "cipher": "aes-128-ctr", 103 | "cipherparams": { "iv": "fcf270e966455dc752293b025e40952c" }, 104 | "ciphertext": "60fd77dfdde7c8d66a20513577f55eb26064bbfe5cccf1a903d3c0d98430bbb9", 105 | "kdf": "scrypt", 106 | "kdfparams": { 107 | "salt": "90fac00812c62ad0d6c239fd9d7acb2141beada793fce2be7a30f3bafa546eb5", 108 | "n": 8192, 109 | "c": 262144, 110 | "r": 8, 111 | "p": 1, 112 | "dklen": 32 113 | }, 114 | "mac": "ee4f168e2790dc3d5310c7e0a0dbed3c0e84d4561e8bc0c1cfb41bd28b2cfa0f" 115 | }, 116 | "id": "32623434-6237-4165-a139-333435386137", 117 | "version": 3 118 | }, 119 | "privateKey": "59bab003c6471463001aa76cb9766f8a3dd13ab04c5f21b6ce3323cb7b2858d5", 120 | "passphrase": "feea9a68565082c2" 121 | }, 122 | { 123 | "keystore": { 124 | "address": "1cf63573ccf4c164d47ad7dd6b3856a3d4115181", 125 | "crypto": { 126 | "cipher": "aes-128-ctr", 127 | "cipherparams": { "iv": "9c357b2376988ec17d20ae5db20fae75" }, 128 | "ciphertext": "3c1365cf6ca58a1d1332fef4c276f80918d85d91ffa72602b59d2d07cb9c2b01", 129 | "kdf": "scrypt", 130 | "kdfparams": { 131 | "salt": "f0444e2b8b7570e29761b20d8e8679115c0691ed6893c757b38f5b62c90309a0", 132 | "n": 8192, 133 | "c": 262144, 134 | "r": 8, 135 | "p": 1, 136 | "dklen": 32 137 | }, 138 | "mac": "af0393f11487d28e51ff74b4014575f655f07fb5bd6ac5fc2ab551f965d72f65" 139 | }, 140 | "id": "33643932-3966-4138-a362-633562656533", 141 | "version": 3 142 | }, 143 | "privateKey": "f1092263ed2631867d450a1fea54dab348a88590af3cf35194afb2fb74fec60e", 144 | "passphrase": "b8c2d76d90603184" 145 | }, 146 | { 147 | "keystore": { 148 | "address": "b032929fda068344c06390886851b58618e0d371", 149 | "crypto": { 150 | "cipher": "aes-128-ctr", 151 | "cipherparams": { "iv": "3c8e3afd22737e5a808de1229c1df481" }, 152 | "ciphertext": "9f52a8a5d6a7c97fc214c12d38e04fe306af300951cffa84ed28e67d2f1319e5", 153 | "kdf": "scrypt", 154 | "kdfparams": { 155 | "salt": "8fe1ffa510bc5cbfa2d66da5936fec6c4245ab6e8a9d18b5729d90cb5fe6150d", 156 | "n": 8192, 157 | "c": 262144, 158 | "r": 8, 159 | "p": 1, 160 | "dklen": 32 161 | }, 162 | "mac": "54b181870cf5e2554c1286b834358ac307a18f6e0e34603ca1396631b0336f58" 163 | }, 164 | "id": "61663231-3035-4734-b431-636139323461", 165 | "version": 3 166 | }, 167 | "privateKey": "d8a8e570210d5bd6fd18d4c329c350dcbacd561b370b8f44ea82854be125c9fd", 168 | "passphrase": "79aef1be9c21fa6d" 169 | }, 170 | { 171 | "keystore": { 172 | "address": "716b269cc1e16af73ec06c11b56ff4473ffb90c2", 173 | "crypto": { 174 | "cipher": "aes-128-ctr", 175 | "cipherparams": { "iv": "a91eb27262d258fda2b659ba700dbabf" }, 176 | "ciphertext": "071a026788102ac115b385ecfa44a94308ae6e9d9bf3195b33afd5011c092883", 177 | "kdf": "scrypt", 178 | "kdfparams": { 179 | "salt": "fd18fe38f45ec48bb645f4f8c78034fd67090a7e85d31d8d2c9ab202c1690d2c", 180 | "n": 8192, 181 | "c": 262144, 182 | "r": 8, 183 | "p": 1, 184 | "dklen": 32 185 | }, 186 | "mac": "fbcd3459a821cee747dbab29f377955605a746cb296526bed21f51f03fbc7ae3" 187 | }, 188 | "id": "66313337-6239-4362-b131-336466653837", 189 | "version": 3 190 | }, 191 | "privateKey": "d839036075bdde6d5e80dbd62224c62015d2134675d0b3a2b952a3946151381f", 192 | "passphrase": "74df0c784b9ca723" 193 | }, 194 | { 195 | "keystore": { 196 | "address": "5ac3b3f9ece121588d4b4d8369fd17991e9faa45", 197 | "crypto": { 198 | "cipher": "aes-128-ctr", 199 | "cipherparams": { "iv": "c2666e0f3733618a9eaebdebcd954b63" }, 200 | "ciphertext": "021e6c2cd087e60e94d3cd3cb9395883dc469cc371583146ef71c3959d6a22fd", 201 | "kdf": "scrypt", 202 | "kdfparams": { 203 | "salt": "a72419db32d1f2ba87bd05686966249cc517512406b31e0e746f47a2c3423a49", 204 | "n": 8192, 205 | "c": 262144, 206 | "r": 8, 207 | "p": 1, 208 | "dklen": 32 209 | }, 210 | "mac": "4579859d1a81a73dd0e76d128685f3e26ad3ead30e685c413f45510176b9a8d8" 211 | }, 212 | "id": "33356632-6136-4362-b939-356137313065", 213 | "version": 3 214 | }, 215 | "privateKey": "e94de67e79c60ee1db62a7aa505a67135e172156a5abf587150b6cc6eb9b08bb", 216 | "passphrase": "cf4b98b6284a54fa" 217 | }, 218 | { 219 | "keystore": { 220 | "address": "939dc05470c4a14da675393238c6f8cee5a175e9", 221 | "crypto": { 222 | "cipher": "aes-128-ctr", 223 | "cipherparams": { "iv": "c9cceb96cd2c5438d443715fec7c79a0" }, 224 | "ciphertext": "34d308a6cebb01e7558832ee18e394b320d180d2b80387cafc950bcd080121c0", 225 | "kdf": "scrypt", 226 | "kdfparams": { 227 | "salt": "6c942b39cbd7eb4c024a8ae364d5f7bf80c172236df5e8fdb3a57ff09acb829a", 228 | "n": 8192, 229 | "c": 262144, 230 | "r": 8, 231 | "p": 1, 232 | "dklen": 32 233 | }, 234 | "mac": "1ce129d11292d0a0fbc8269a0d1b85326e2e8fb0f241e2e145db6b6978a0e668" 235 | }, 236 | "id": "61623230-3232-4430-a262-333930316261", 237 | "version": 3 238 | }, 239 | "privateKey": "268a3c5292823e497bd25aea381e22688bff45c47676d8890cf33b8daebf09a6", 240 | "passphrase": "09069a0662a617b0" 241 | } 242 | ] 243 | -------------------------------------------------------------------------------- /test/key_gen.dart: -------------------------------------------------------------------------------- 1 | @TestOn("vm") 2 | 3 | import "package:test/test.dart"; 4 | import 'package:laksadart/src/crypto/schnorr.dart' as crypto; 5 | 6 | main() { 7 | test('test with 1000 keypairs', () { 8 | for (int i = 0; i < 1000; i++) { 9 | String prvKey = crypto.generatePrivateKey(); 10 | String pubKey = crypto.getPubKeyFromPrivateKey(prvKey); 11 | String address = crypto.getAddressFromPrivateKey(prvKey); 12 | 13 | expect(prvKey.length, equals(64)); 14 | expect(pubKey.length, equals(66)); 15 | expect(address.length, equals(40)); 16 | } 17 | }); 18 | } 19 | -------------------------------------------------------------------------------- /test/keystore.dart: -------------------------------------------------------------------------------- 1 | @TestOn("vm") 2 | 3 | import 'dart:convert'; 4 | import 'dart:io'; 5 | import "package:test/test.dart"; 6 | import "package:laksadart/src/account/account.dart"; 7 | 8 | main() { 9 | test("Test decrypt KeyStores", () async { 10 | File schnorrVector = new File('test/fixtures/keystores.json'); 11 | 12 | await schnorrVector 13 | .readAsString() 14 | .then((fileContents) => jsonDecode(fileContents)) 15 | .then((keystoreFixture) async { 16 | for (int i = 0; i < keystoreFixture.length; i++) { 17 | var privateKey = keystoreFixture[i]['privateKey']; 18 | var passphrase = keystoreFixture[i]['passphrase']; 19 | var keystore = json.encode(keystoreFixture[i]['keystore']); 20 | Account account = await Account.fromFile(keystore, passphrase); 21 | expect(account.privateKey, equals(privateKey)); 22 | } 23 | }); 24 | }); 25 | test("Test encrypt KeyStores", () async { 26 | File schnorrVector = new File('test/fixtures/keystores.json'); 27 | 28 | await schnorrVector 29 | .readAsString() 30 | .then((fileContents) => jsonDecode(fileContents)) 31 | .then((keystoreFixture) async { 32 | for (int i = 0; i < keystoreFixture.length; i++) { 33 | var privateKey = keystoreFixture[i]['privateKey']; 34 | var passphrase = keystoreFixture[i]['passphrase']; 35 | Account testAccount = new Account(privateKey); 36 | String encrypted = await testAccount.toFile(passphrase); 37 | expect(json.decode(encrypted)['mac'], 38 | equals(keystoreFixture[i]['keystore']['mac'])); 39 | } 40 | }); 41 | }); 42 | } 43 | -------------------------------------------------------------------------------- /test/proto.dart: -------------------------------------------------------------------------------- 1 | @TestOn("vm") 2 | 3 | import "package:test/test.dart"; 4 | import 'package:laksadart/src/utils/numbers.dart' as numbers; 5 | import 'package:laksadart/src/utils/transaction.dart'; 6 | 7 | void main() { 8 | test('test protobuf encode transaction', () { 9 | Map txn = { 10 | 'version': 0, 11 | 'nonce': 0, 12 | 'toAddr': '2E3C9B415B19AE4035503A06192A0FAD76E04243', 13 | 'pubKey': 14 | '0246e7178dc8253201101e18fd6f6eb9972451d121fc57aa2a06dd5c111e58dc6a', 15 | 'amount': BigInt.from(10000), 16 | 'gasPrice': BigInt.from(100), 17 | 'gasLimit': 1000, 18 | 'code': '(* HelloWorld contract *)', 19 | 'data': '[{"vname":"fuck"}]' 20 | }; 21 | var encodedBuffer = encodeTransactionProto(txn); 22 | var expectedResult = 23 | '080010001a142e3c9b415b19ae4035503a06192a0fad76e0424322230a210246e7178dc8253201101e18fd6f6eb9972451d121fc57aa2a06dd5c111e58dc6a2a120a100000000000000000000000000000271032120a100000000000000000000000000000006438e8074219282a2048656c6c6f576f726c6420636f6e7472616374202a294a125b7b22766e616d65223a226675636b227d5d'; 24 | expect(numbers.bytesToHex(encodedBuffer), equals(expectedResult)); 25 | }); 26 | } 27 | -------------------------------------------------------------------------------- /test/schnorr.dart: -------------------------------------------------------------------------------- 1 | @TestOn("vm") 2 | 3 | import 'dart:convert'; 4 | import 'dart:io'; 5 | import "package:test/test.dart"; 6 | import 'package:laksadart/src/utils/numbers.dart' as numbers; 7 | import 'package:laksadart/src/crypto/schnorr.dart' as schnorr; 8 | 9 | void main() { 10 | test("Test Schnorr Signature with preset json", () async { 11 | File schnorrVector = new File('test/fixtures/schnorr.signature.json'); 12 | await schnorrVector 13 | .readAsString() 14 | .then((fileContents) => jsonDecode(fileContents)) 15 | .then((testJson) async { 16 | // error i=284 17 | for (int i = 285; i < 1000; i++) { 18 | String pub = testJson[i]['pub']; 19 | String? priv = testJson[i]['priv']; 20 | String msg = testJson[i]['msg']; 21 | String? k = testJson[i]['k']; 22 | String r = testJson[i]['r']; 23 | String s = testJson[i]['s']; 24 | 25 | dynamic sig = null; 26 | 27 | while (sig == null) { 28 | sig = await schnorr.trySign( 29 | numbers.hexToBytes(msg), 30 | numbers.hexToInt(k!), 31 | numbers.hexToInt(priv!), 32 | numbers.hexToBytes(pub)); 33 | } 34 | 35 | // check with c++ definition 36 | bool res = await schnorr.verify( 37 | numbers.hexToBytes(msg), 38 | sig.r, 39 | sig.s, 40 | numbers.hexToBytes(pub), 41 | ); 42 | 43 | expect(res, equals(true)); 44 | expect(sig.s, equals(numbers.hexToInt(s))); 45 | expect(sig.r, equals(numbers.hexToInt(r))); 46 | } 47 | }); 48 | }); 49 | } 50 | --------------------------------------------------------------------------------