Skip to content

Commit

Permalink
Merge pull request #10 from Tezsure/dev
Browse files Browse the repository at this point in the history
Tezster_dart version 2.0.0 release
  • Loading branch information
yoyodefi authored Jan 13, 2021
2 parents f03d51d + 952428f commit 1ec9b77
Show file tree
Hide file tree
Showing 22 changed files with 951 additions and 3 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
# [2.0.0]

* Dependencies update.
* Added Get Balance.
* Added Transfer Balance.
* Added Delegate an Account.

# [1.0.1]

* Dependencies update.
Expand Down
68 changes: 68 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,14 @@ Tezos is a decentralized blockchain that governs itself by establishing a true d
### Features

* Tezos wallet utilities.
* Get Balance.
* Generate mnemonics.
* Generate keys from mnemonic.
* Generate keys from mnemonics and passphrase.
* Sign Operation Group.
* Unlock fundraiser identity.
* Transfer Balance.
* Delegate an Account.

### Getting started

Expand All @@ -32,6 +35,12 @@ import 'package:tezster_dart/tezster_dart.dart';

### Usage

* Get Balance

``` dart
String balance = await TezsterDart.getBalance('tz1c....ozGGs', 'your rpc server');
```

* Generate mnemonic

``` dart
Expand Down Expand Up @@ -86,6 +95,65 @@ List<String> identityFundraiser = await TezsterDart.unlockFundraiserIdentity(
tz1hhkSbaocSWm3wawZUuUdX57L3maSH16Pv] */
```

* Transfer Balance.
* The most basic operation on the chain is the transfer of value between two accounts. In this example we have the account we activated above: tz1QSHaKpTFhgHLbqinyYRjxD5sLcbfbzhxy and some random testnet address to test with: tz1RVcUP9nUurgEJMDou8eW3bVDs6qmP5Lnc. Note all amounts are in µtz, as in micro-tez, hence 0.5tz is represented as 500000. The fee of 1500 was chosen arbitrarily, but some operations have minimum fee requirements.

``` dart
var server = '';
var keyStore = KeyStoreModel(
publicKey: 'edpkvQtuhdZQmjdjVfaY9Kf4hHfrRJYugaJErkCGvV3ER1S7XWsrrj',
secretKey:
'edskRgu8wHxjwayvnmpLDDijzD3VZDoAH7ZLqJWuG4zg7LbxmSWZWhtkSyM5Uby41rGfsBGk4iPKWHSDniFyCRv3j7YFCknyHH',
publicKeyHash: 'tz1QSHaKpTFhgHLbqinyYRjxD5sLcbfbzhxy',
);
var signer = await TezsterDart.createSigner(
TezsterDart.writeKeyWithHint(keyStore.secretKey, 'edsk'));
var result = await TezsterDart.sendTransactionOperation(
server,
signer,
keyStore,
'tz1RVcUP9nUurgEJMDou8eW3bVDs6qmP5Lnc',
500000,
1500,
);
print("Applied operation ===> $result['appliedOp']");
print("Operation groupID ===> $result['operationGroupID']");
```

* Delegate an Account.
* One of the most exciting features of Tezos is delegation. This is a means for non-"baker" (non-validator) accounts to participate in the on-chain governance process and receive staking rewards. It is possible to delegate both implicit and originated accounts. For implicit addresses, those starting with tz1, tz2 and tz3, simply call sendDelegationOperation. Originated accounts, that is smart contracts, must explicitly support delegate assignment, but can also be deployed with a delegate already set.

``` dart
var server = '';
var keyStore = KeyStoreModel(
publicKey: 'edpkvQtuhdZQmjdjVfaY9Kf4hHfrRJYugaJErkCGvV3ER1S7XWsrrj',
secretKey:
'edskRgu8wHxjwayvnmpLDDijzD3VZDoAH7ZLqJWuG4zg7LbxmSWZWhtkSyM5Uby41rGfsBGk4iPKWHSDniFyCRv3j7YFCknyHH',
publicKeyHash: 'tz1QSHaKpTFhgHLbqinyYRjxD5sLcbfbzhxy',
);
var signer = await TezsterDart.createSigner(
TezsterDart.writeKeyWithHint(keyStore.secretKey, 'edsk'));
var result = await TezsterDart.sendDelegationOperation(
server,
signer,
keyStore,
'tz1RVcUP9nUurgEJMDou8eW3bVDs6qmP5Lnc',
10000,
);
print("Applied operation ===> $result['appliedOp']");
print("Operation groupID ===> $result['operationGroupID']");
```

---
**NOTE:**
Use stable version of flutter to avoid package conflicts.
Expand Down
1 change: 1 addition & 0 deletions example/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
In most cases you can leave this as-is, but you if you want to provide
additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here. -->
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name="io.flutter.app.FlutterApplication"
android:label="example"
Expand Down
41 changes: 41 additions & 0 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,47 @@ class _MyAppState extends State<MyApp> {
print("identityFundraiser ===> $identityFundraiser");
//identityFundraiser ===> [privateKey, publicKey, publicKeyHash]
//Accessing: private key ===> identityFundraiser[0] | public key ===> identityFundraiser[1] | public Key Hash ===> identityFundraiser[2] all of type string.

// Get Balance
String balance =
await TezsterDart.getBalance('tz1c....ozGGs', 'your rpc server');
print("Accoutn Balance ===> $balance");

var server = '';

var keyStore = KeyStoreModel(
publicKey: 'edpkvQtuhdZQmjdjVfaY9Kf4hHfrRJYugaJErkCGvV3ER1S7XWsrrj',
secretKey:
'edskRgu8wHxjwayvnmpLDDijzD3VZDoAH7ZLqJWuG4zg7LbxmSWZWhtkSyM5Uby41rGfsBGk4iPKWHSDniFyCRv3j7YFCknyHH',
publicKeyHash: 'tz1QSHaKpTFhgHLbqinyYRjxD5sLcbfbzhxy',
);

//Send transaction
var transactionSigner = await TezsterDart.createSigner(
TezsterDart.writeKeyWithHint(keyStore.secretKey, 'edsk'));
var transactionResult = await TezsterDart.sendTransactionOperation(
server,
transactionSigner,
keyStore,
'tz1RVcUP9nUurgEJMDou8eW3bVDs6qmP5Lnc',
500000,
1500,
);
print("Applied operation ===> $transactionResult['appliedOp']");
print("Operation groupID ===> $transactionResult['operationGroupID']");

//Send delegation
var delegationSigner = await TezsterDart.createSigner(
TezsterDart.writeKeyWithHint(keyStore.secretKey, 'edsk'));
var delegationResult = await TezsterDart.sendDelegationOperation(
server,
delegationSigner,
keyStore,
'tz1RVcUP9nUurgEJMDou8eW3bVDs6qmP5Lnc',
10000,
);
print("Applied operation ===> $delegationResult['appliedOp']");
print("Operation groupID ===> $delegationResult['operationGroupID']");
}

@override
Expand Down
2 changes: 1 addition & 1 deletion example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ packages:
path: ".."
relative: true
source: path
version: "1.0.1"
version: "2.0.0"
typed_data:
dependency: transitive
description:
Expand Down
50 changes: 50 additions & 0 deletions lib/chain/tezos/tezos_message_codec.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import 'package:tezster_dart/chain/tezos/tezos_message_utils.dart';
import 'package:tezster_dart/models/operation_model.dart';

class TezosMessageCodec {
static String encodeOperation(OperationModel message) {
if (message.kind == 'transaction') return encodeTransaction(message);
if (message.kind == 'reveal') return encodeReveal(message);
if (message.kind == 'delegation') return encodeDelegation(message);
}

static String encodeTransaction(OperationModel message) {
String hex = TezosMessageUtils.writeInt(108);
hex += TezosMessageUtils.writeAddress(message.source).substring(2);
hex += TezosMessageUtils.writeInt(int.parse(message.fee));
hex += TezosMessageUtils.writeInt(message.counter);
hex += TezosMessageUtils.writeInt(message.gasLimit);
hex += TezosMessageUtils.writeInt(message.storageLimit);
hex += TezosMessageUtils.writeInt(int.parse(message.amount));
hex += TezosMessageUtils.writeAddress(message.destination);
hex += '00';
return hex;
}

static String encodeReveal(OperationModel message) {
var hex = TezosMessageUtils.writeInt(107); //sepyTnoitarepo['reveal']);
hex += TezosMessageUtils.writeAddress(message.source).substring(2);
hex += TezosMessageUtils.writeInt(int.parse(message.fee));
hex += TezosMessageUtils.writeInt(message.counter);
hex += TezosMessageUtils.writeInt(message.gasLimit);
hex += TezosMessageUtils.writeInt(message.storageLimit);
hex += TezosMessageUtils.writePublicKey(message.publicKey);
return hex;
}

static String encodeDelegation(OperationModel delegation) {
var hex = TezosMessageUtils.writeInt(110);
hex += TezosMessageUtils.writeAddress(delegation.source).substring(2);
hex += TezosMessageUtils.writeInt(int.parse(delegation.fee));
hex += TezosMessageUtils.writeInt(delegation.counter);
hex += TezosMessageUtils.writeInt(delegation.gasLimit);
hex += TezosMessageUtils.writeInt(delegation.storageLimit);
if (delegation.delegate != null && delegation.delegate.isNotEmpty) {
hex += TezosMessageUtils.writeBoolean(true);
hex += TezosMessageUtils.writeAddress(delegation.delegate).substring(2);
} else {
hex += TezosMessageUtils.writeBoolean(false);
}
return hex;
}
}
104 changes: 104 additions & 0 deletions lib/chain/tezos/tezos_message_utils.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import 'dart:typed_data';

import 'package:blake2b/blake2b_hash.dart';
import 'package:bs58check/bs58check.dart';
import 'package:convert/convert.dart';
import 'package:tezster_dart/helper/generateKeys.dart';
import 'package:tezster_dart/src/soft-signer/soft_signer.dart';

class TezosMessageUtils {
static String writeBranch(String branch) {
return hex.encode(base58
.decode(branch)
.sublist(2, base58.decode(branch).length - 4)
.toList());
// return hex.encode(base58.decode(branch).sublist(2).toList());
}

static String writeInt(int value) {
if (value < 0) {
throw new Exception('Use writeSignedInt to encode negative numbers');
}
var byteHexList = Uint8List.fromList(hex.decode(twoByteHex(value)));
for (var i = 0; i < byteHexList.length; i++) {
if (i != 0) byteHexList[i] ^= 0x80;
}
var result = hex.encode(byteHexList.reversed.toList());
return result;
}

static String twoByteHex(int n) {
if (n < 128) {
var s = ('0' + n.toRadixString(16));
return s.substring(s.length - 2);
}
String h = '';
if (n > 2147483648) {
var r = BigInt.from(n);
while (r.compareTo(BigInt.zero) != -1) {
var _h = ('0' + (r & BigInt.from(127)).toRadixString(16));
h = _h.substring(_h.length - 2) + h;
r = r >> 7;
}
} else {
var r = n;
while (r > 0) {
var _h = ('0' + (r & 127).toRadixString(16));
h = _h.substring(_h.length - 2) + h;
r = r >> 7;
}
}
return h;
}

static String writeAddress(String address) {
var base58data = base58.decode(address).sublist(3);
base58data = base58data.sublist(0, base58data.length - 4);
var _hex = hex.encode(base58data);
if (address.startsWith("tz1")) {
return "0000" + _hex;
} else if (address.startsWith("tz2")) {
return "0001" + _hex;
} else if (address.startsWith("tz3")) {
return "0002" + _hex;
} else if (address.startsWith("KT1")) {
return "01" + _hex + "00";
} else {
throw new Exception(
'Unrecognized address prefix: ${address.substring(0, 3)}');
}
}

static String writePublicKey(String publicKey) {
if (publicKey.startsWith("edpk")) {
return "00" + hex.encode(base58.decode(publicKey).sublist(4));
} else if (publicKey.startsWith("sppk")) {
return "01" + hex.encode(base58.decode(publicKey).sublist(4));
} else if (publicKey.startsWith("p2pk")) {
return "02" + hex.encode(base58.decode(publicKey).sublist(4));
} else {
throw new Exception('Unrecognized key type');
}
}

static Uint8List simpleHash(Uint8List message, int size) {
return Uint8List.fromList(Blake2bHash.hashWithDigestSize(256, message));
}

static String readSignatureWithHint(Uint8List opSignature, SignerCurve hint) {
opSignature = Uint8List.fromList(opSignature);
if (hint == SignerCurve.ED25519) {
return GenerateKeys.readKeysWithHint(opSignature, '09f5cd8612');
} else if (hint == SignerCurve.SECP256K1) {
return GenerateKeys.readKeysWithHint(opSignature, '0d7365133f');
} else if (hint == SignerCurve.SECP256R1) {
return GenerateKeys.readKeysWithHint(opSignature, '36f02c34');
} else {
throw Exception('Unrecognized signature hint, "$hint"');
}
}

static String writeBoolean(bool b) {
return b ? "ff" : "00";
}
}
45 changes: 45 additions & 0 deletions lib/chain/tezos/tezos_node_reader.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@

import 'package:tezster_dart/helper/http_helper.dart';

class TezosNodeReader {
static Future<int> getCounterForAccount(String server, String publicKeyHash,
{String chainid = 'main'}) async {
var response = await HttpHelper.performGetRequest(server,
'chains/$chainid/blocks/head/context/contracts/$publicKeyHash/counter');
return int.parse(response.toString().replaceAll('"', ''), radix: 10);
}

static Future<bool> isManagerKeyRevealedForAccount(
String server, String publicKeyHash) async {
var managerKey =
await getAccountManagerForBlock(server, 'head', publicKeyHash);
return managerKey.length > 0;
}

static Future<String> getAccountManagerForBlock(
String server, String block, String publicKeyHash,
{String chainid = 'main'}) async {
var response = await HttpHelper.performGetRequest(server,
'chains/$chainid/blocks/$block/context/contracts/$publicKeyHash/manager_key');
return response.toString().isNotEmpty ? response.toString() : '';
}

static Future<Map<dynamic, dynamic>> getBlockAtOffset(
String server, int offset,
{String chainid = 'main'}) async {
if (offset <= 0) {
return await getBlock(server);
}
var head = await getBlock(server);
var response = await HttpHelper.performGetRequest(
server, 'chains/$chainid/blocks/${head['header']['level'] - offset}');
return response;
}

static Future<Map<dynamic, dynamic>> getBlock(String server,
{String hash = 'head', String chainid = 'main'}) async {
var response = await HttpHelper.performGetRequest(
server, 'chains/$chainid/blocks/$hash');
return response;
}
}
Loading

0 comments on commit 1ec9b77

Please sign in to comment.