Dart package for interfacing with the Bluzelle database service, modelled after @bluzelle/sdk-js.
Video Demo: https://youtu.be/PR5JIAzcqrU
bluzelle
is currently not available on pub.dev but you can use it by referencing this repository in your pubspec.yaml
as shown below:
dependencies:
bluzelle:
git: https://github.com/Echolon166/bluzelle_dart.git
You can optionally pin your dependency to a specific commit, branch, or tag to avoid possible breaking changes:
dependencies:
bluzelle:
git:
url: https://github.com/Echolon166/bluzelle_dart.git
ref: some-branch
In order to connect your instance to the Bluzelle testnet, you can get a new account by:
-
Mint an account by visiting https://client.sentry.testnet.private.bluzelle.com:1317/mint, which will provide a mnemonic and an address. This may take a while.
-
Check your balance at https://client.sentry.testnet.private.bluzelle.com:1317/bank/balances/{address}. If your account balance is 0, mint another account until a positive ubnt balance shows.
Once you have your account and the package, you can create an sdk instance with:
import 'package:bluzelle/bluzelle.dart';
void main() {
final sdk = bluzelle(
mnemonic: mnemonic_from_mint_endpoint,
host : 'wss://client.sentry.testnet.private.bluzelle.com',
port: 26657, // Optional. Defaults to 26657.
maxGas: 100000000,
gasPrice: 0.002,
);
}
*Note: If the specified maxGas and/or gasPrice is too low, any transaction may be rejected by a validator (e.g. a transaction requires more gas than maxGas specified, or the gasPrice is too low to cover validator fees). The default suggestion for these fields above will suffice.
- Currently the sdk supports both websocket and https connections to the Bluzelle testnet.
- For https pass the host https://client.sentry.testnet.private.bluzelle.com to the bluzelle constructor.
- Use websockets (wss://client.sentry.testnet.private.bluzelle.com) for greater performance.
- Websocket connections can run indefinitely if not closed properly so, be sure to call
sdk.db.close()
at the very end of your application to shut them down properly.
- Websocket connections can run indefinitely if not closed properly so, be sure to call
After configuring your sdk, you will have access to various modules and their corresponding methods.
- Hierarchical format:
sdk.[module].[q or tx or field].[method](ClientContext, MethodRequest(...request fields))
- Available Modules: db, bank
- Available Fields: url, address, withTransactions()
Each method takes two parameters(ClientContext and MethodRequest) as objects (i.e. Method(ClientContext, MethodRequest)), and returns an object (i.e. MethodResponse). To see the MethodRequest and MethodResponse types, see the /proto/[module] for queries and transactions.
*Note: ClientContext is used to set timeout of the RPC call, this method is not included in the library and can be passed as a null
.
**Note: See dart-lang/protobuf for further information.
- Crud module query:
sdk.db.q.read(
null,
QueryReadRequest( // Takes a MsgCreate object.
uuid: 'uuid',
key: 'myKey',
),
)
.then((resp) => resp.value);
// Returns a future of a QueryReadResponse object*.
*Note: resp.value is a List<int> representing the byte-encoded value that had been queried. To get the string-representation of the value, use utf8.decode(resp.value).
- Bank module query:
sdk.bank.q.balance(
null,
QueryBalanceRequest(
address: sdk.bank.address, // You can access your sdk's bluzelle address.
denom: 'ubnt',
),
);
The sdk can also send transactions to the chain. Each module has a tx method to send various transaction messages.
- Crud module tx:
sdk.db.tx.create_(
null,
MsgCreate( // Takes a MsgCreate object.
creator: sdk.db.address, // The creator of the transaction should always be the sender's address*.
uuid: 'uuid',
key: 'myKey',
value: utf8.encode('myValue'), // Values are stored as byte lists.
metadata: [],
lease: Lease(seconds: 0, minutes: 0, hours: 1, days: 0, years: 0), // Lease object to specify lifespan of key-value**.
),
);
// Returns a future of a MsgCreateResponse.
*Note: The sdk is signing and sending the transaction, so the signer address must match the creator of the transaction. Otherwise, an error will be thrown.
**Note: See lease.pb.dart to see the Lease class.
- Bank module tx:
sdk.bank.tx.send(
null,
MsgSend(
amount: [
Coin(
denom: 'ubnt',
amount: '300',
),
],
fromAddress: sdk.bank.address,
toAddress: [some_bluzelle_address],
),
);
Wrap multiple messages in a single transaction.
sdk.db.withTransactions(
[
MsgCreate(
creator: sdk.db.address,
uuid: uuid,
key: 'firstKey',
value: utf8.encode('firstValue'),
metadata: [],
lease: Lease(seconds: 0, minutes: 0, hours: 1, days: 0, years: 0),
),
MsgCreate(
creator: sdk.db.address,
uuid: uuid,
key: 'secondKey',
value: utf8.encode('secondValue'),
metadata: [],
lease: Lease(seconds: 0, minutes: 0, hours: 1, days: 0, years: 0),
),
MsgRenewLeasesAll(
creator: sdk.db.address,
uuid: uuid,
lease: Lease(seconds: 0, minutes: 0, hours: 1, days: 0, years: 0),
)
],
memo: 'optionalMemo', // Optional.
);
*Note: If any of the messages which are passed to withTransactions fail, then all messages will fail and not be committed to a block.
- Transactions
- Queries
Create a key-value in the database.
final createResponse = await sdk.db.tx.create_(
null,
MsgCreate(
creator: sdk.db.address,
uuid: 'uuid',
key: 'myKey',
value: utf8.encode('myValue'),
metadata: [],
lease: Lease(seconds: 0, minutes: 0, hours: 1, days: 0, years: 0),
),
);
.....
sdk.db.tx.create_(
null,
MsgCreate(
creator: sdk.db.address,
uuid: 'uuid',
key: 'myKey',
value: utf8.encode('myValue'),
metadata: [],
lease: Lease(seconds: 0, minutes: 0, hours: 1, days: 0, years: 0),
),
)
.then((resp) => {...})
.catchError((err) => {...});
Returns: Future<MsgCreateResponse> (empty object)
MsgCreate | Description | Type |
---|---|---|
creator | Signer address | String |
uuid | Database identifier | String |
key | String | |
value | List<int> | |
metadata | List<int> | |
lease | Key-value life-span | Lease * |
*Lease {seconds: int, minutes: int, hours: int, days: int, years: int}
Delete a key-value in the database.
final deleteResponse = await sdk.db.tx.delete(
null,
MsgDelete(
creator: sdk.db.address,
uuid: 'uuid',
key: 'myKey',
),
);
.....
sdk.db.tx.delete(
null,
MsgDelete(
creator: sdk.db.address,
uuid: 'uuid',
key: 'myKey',
),
)
.then((resp) => {...})
.catchError((err) => {...});
Returns: Future<MsgDeleteResponse> (empty object)
MsgDelete | Description | Type |
---|---|---|
creator | Signer address | String |
uuid | Database identifier | String |
key | Key to delete | String |
Delete all key-values in the database.
final deleteAllResponse = await sdk.db.tx.deleteAll(
null,
MsgDeleteAll(
creator: sdk.db.address,
uuid: 'uuid',
),
);
.....
sdk.db.tx.deleteAll(
null,
MsgDeleteAll(
creator: sdk.db.address,
uuid: 'uuid',
),
)
.then((resp) => {...})
.catchError((err) => {...});
Returns: Future<MsgDeleteAllResponse> (empty object)
MsgDeleteAll | Description | Type |
---|---|---|
creator | Signer address | String |
uuid | Database identifier | String |
Update a set of key-values in the specified uuid.
final multiUpdateResponse = await sdk.db.tx.multiUpdate(
null,
MsgMultiUpdate(
creator: sdk.db.address,
uuid: 'uuid',
keyValues: [
KeyValueLease(
key: 'existingKey1',
value: utf8.encode('newValue1'),
lease: Lease(seconds: 0, minutes: 0, hours: 1, days: 0, years: 0),
),
KeyValueLease(
key: 'existingKey2',
value: utf8.encode('newValue2'),
lease: Lease(seconds: 0, minutes: 0, hours: 1, days: 0, years: 0),
),
],
),
);
.....
sdk.db.tx.multiUpdate(
null,
MsgMultiUpdate(
creator: sdk.db.address,
uuid: 'uuid',
keyValues: [
KeyValueLease(
key: 'existingKey1',
value: utf8.encode('newValue1'),
lease: Lease(seconds: 0, minutes: 0, hours: 1, days: 0, years: 0),
),
KeyValueLease(
key: 'existingKey2',
value: utf8.encode('newValue2'),
lease: Lease(seconds: 0, minutes: 0, hours: 1, days: 0, years: 0),
),
],
),
)
.then((resp) => {...})
.catchError((err) => {...});
Returns: Future<MsgMultiUpdateResponse> (empty object)
MsgMultiUpdate | Description | Type |
---|---|---|
creator | Signer address | String |
uuid | Database identifier | String |
keyValues | Iterable<KeyValueLease *> |
* KeyValueLease {key: String, value: List<int>, lease: Lease **}
**Lease {seconds: int, minutes: int, hours: int, days: int, years: int}
Rename a key-value in the database.
final renameResponse = await sdk.db.tx.rename(
null,
MsgRename(
creator: sdk.db.address,
uuid: 'uuid',
key: 'existingKey',
newKey: 'renamingKey',
),
);
.....
sdk.db.tx.rename(
null,
MsgRename(
creator: sdk.db.address,
uuid: 'uuid',
key: 'existingKey',
newKey: 'renamingKey',
),
)
.then((resp) => {...})
.catchError((err) => {...});
Returns: Future<MsgRenameResponse> (empty object)
MsgRename | Description | Type |
---|---|---|
creator | Signer address | String |
uuid | Database identifier | String |
key | Existing key | String |
newKey | New key used to rename | String |
Renew the lease of a key-value in the database.
final renewLeaseResponse = await sdk.db.tx.renewLease(
null,
MsgRenewLease(
creator: sdk.db.address,
uuid: 'uuid',
key: 'existingKey',
lease: Lease(seconds: 0, minutes: 0, hours: 1, days: 0, years: 0),
),
);
.....
sdk.db.tx.renewLease(
null,
MsgRenewLease(
creator: sdk.db.address,
uuid: 'uuid',
key: 'existingKey',
lease: Lease(seconds: 0, minutes: 0, hours: 1, days: 0, years: 0),
),
)
.then((resp) => {...})
.catchError((err) => {...});
Returns: Future<MsgRenewLeaseResponse> (empty object)
MsgRenewLease | Description | Type |
---|---|---|
creator | Signer address | String |
uuid | Database identifier | String |
key | String | |
lease | New life-span for key-value | Lease * |
*Lease {seconds: int, minutes: int, hours: int, days: int, years: int}
Renew all the leases of key-values in the specified uuid.
final renewLeasesAllResponse = await sdk.db.tx.renewLeasesAll(
null,
MsgRenewLeasesAll(
creator: sdk.db.address,
uuid: 'uuid',
lease: Lease(seconds: 0, minutes: 0, hours: 1, days: 0, years: 0),
),
);
.....
sdk.db.tx.renewLeasesAll(
null,
MsgRenewLeasesAll(
creator: sdk.db.address,
uuid: 'uuid',
lease: Lease(seconds: 0, minutes: 0, hours: 1, days: 0, years: 0),
),
)
.then((resp) => {...})
.catchError((err) => {...});
Returns: Future<MsgRenewLeasesAllResponse> (empty object)
MsgRenewLeasesAll | Description | Type |
---|---|---|
creator | Signer address | String |
uuid | Database identifier | String |
lease | New life-span for all key-values | Lease * |
*Lease {seconds: int, minutes: int, hours: int, days: int, years: int}
Update a key-value in the database.
final updateResponse = await sdk.db.tx.update(
null,
MsgUpdate(
creator: sdk.db.address,
uuid: 'uuid',
key: 'existingKey',
value: utf8.encode('newValue'),
metadata: [],
lease: Lease(seconds: 0, minutes: 0, hours: 1, days: 0, years: 0),
),
);
.....
sdk.db.tx.update(
null,
MsgUpdate(
creator: sdk.db.address,
uuid: 'uuid',
key: 'existingKey',
value: utf8.encode('newValue'),
metadata: [],
lease: Lease(seconds: 0, minutes: 0, hours: 1, days: 0, years: 0),
),
)
.then((resp) => {...})
.catchError((err) => {...});
Returns: Future<MsgUpdateResponse> (empty object)
MsgUpdate | Description | Type |
---|---|---|
creator | Signer address | String |
uuid | Database identifier | String |
key | String | |
value | New value to update to | List<int> |
metadata | List<int> | |
lease | Key-value life-span | Lease * |
*Lease {seconds: int, minutes: int, hours: int, days: int, years: int}
Upsert a key-value in the database: create a key-value if the key doesn't exist, update the key-value if the key exists.
final upsertResponse = await sdk.db.tx.upsert(
null,
MsgUpsert(
creator: sdk.db.address,
uuid: 'uuid',
key: 'keyToUpsert',
value: utf8.encode('valueToUpsert'),
metadata: [],
lease: Lease(seconds: 0, minutes: 0, hours: 1, days: 0, years: 0),
),
);
.....
sdk.db.tx.upsert(
null,
MsgUpsert(
creator: sdk.db.address,
uuid: 'uuid',
key: 'keyToUpsert',
value: utf8.encode('valueToUpsert'),
metadata: [],
lease: Lease(seconds: 0, minutes: 0, hours: 1, days: 0, years: 0),
),
)
.then((resp) => {...})
.catchError((err) => {...});
Returns: Future<MsgUpsertResponse> (empty object)
MsgUpsert | Description | Type |
---|---|---|
creator | Signer address | String |
uuid | Database identifier | String |
key | String | |
value | List<int> | |
metadata | List<int> | |
lease | Key-value life-span | Lease * |
*Lease {seconds: int, minutes: int, hours: int, days: int, years: int}
Query the total number of key-values in the specified uuid.
final countResponse = await sdk.db.q.count(
null,
QueryCountRequest(
uuid: 'uuid',
),
);
.....
sdk.db.q.count(
null,
QueryCountRequest(
uuid: 'uuid',
),
)
.then((resp) => {...})
.catchError((err) => {...});
Returns: Future<QueryCountResponse>
QueryCountRequest | Description | Type |
---|---|---|
uuid | Database identifier | String |
QueryCountResponse | Description | Type |
---|---|---|
count | Number of key-values in the uuid | int |
Get the remaining lease time of a key-value.
final getLeaseResponse = await sdk.db.q.getLease(
null,
QueryGetLeaseRequest(
uuid: 'uuid',
key: 'myKey',
),
);
.....
sdk.db.q.getLease(
null,
QueryGetLeaseRequest(
uuid: 'uuid',
key: 'myKey',
),
)
.then((resp) => {...})
.catchError((err) => {...});
Returns: Future<QueryGetLeaseResponse>
QueryGetLeaseRequest | Description | Type |
---|---|---|
uuid | Database identifier | String |
key | String |
QueryGetLeaseResponse | Description | Type |
---|---|---|
seconds | Remaining lease time of key-value | int |
Get the remaining lease time of a n key-values.
final getNShortestLeasesResponse = await sdk.db.q.getNShortestLeases(
null,
QueryGetNShortestLeasesRequest(
uuid: 'uuid',
num: 10,
),
);
.....
sdk.db.q.getNShortestLeases(
null,
QueryGetNShortestLeasesRequest(
uuid: 'uuid',
num: 10,
),
)
.then((resp) => {...})
.catchError((err) => {...});
Returns: Future<QueryGetNShortestLeasesResponse>
QueryGetNShortestLeasesRequest | Description | Type |
---|---|---|
uuid | Database identifier | String |
num | Number of keyLeases to return | int |
QueryGetNShortestLeasesResponse | Description | Type |
---|---|---|
keyLeases | Iterable<KeyLease *> |
* KeyLease {key: String, seconds: int}
Check if a key exists in the specified uuid.
final hasResponse = await sdk.db.q.has(
null,
QueryHasRequest(
uuid: 'uuid',
key: 'myKey',
),
);
.....
sdk.db.q.has(
null,
QueryHasRequest(
uuid: 'uuid',
key: 'myKey',
),
)
.then((resp) => {...})
.catchError((err) => {...});
Returns: Future<QueryHasResponse>
QueryHasRequest | Description | Type |
---|---|---|
uuid | Database identifier | String |
key | String |
QueryHasResponse | Description | Type |
---|---|---|
has | True if key exists in uuid; false otherwise | bool |
Read the complete set of keys in the specified uuid.
final keysResponse = await sdk.db.q.keys(
null,
QueryKeysRequest(
uuid: 'uuid',
pagination: PagingRequest(
startKey: 'key-a',
limit: 50.toInt64(),
),
),
);
.....
sdk.db.q.keys(
null,
QueryKeysRequest(
uuid: 'uuid',
pagination: PagingRequest(
startKey: 'key-a',
limit: 50.toInt64(),
),
),
)
.then((resp) => {...})
.catchError((err) => {...});
Returns: Future<QueryKeysResponse>
QueryKeysRequest | Description | Type |
---|---|---|
uuid | Database identifier | String |
pagination (optional) | PagingRequest * |
* PagingRequest {startKey: String, limit: Int64}
QueryKeysResponse | Description | Type |
---|---|---|
keys | Iterable<String> | |
pagination (optional) | PagingResponse * |
* PagingResponse {nextKey: String, total: Int64}
Read the complete set of key-values in the specified uuid.
final keyValuesResponse = await sdk.db.q.keyValues(
null,
QueryKeyValuesRequest(
uuid: 'uuid',
pagination: PagingRequest(
startKey: 'key-a',
limit: 50.toInt64(),
),
),
);
.....
sdk.db.q.keyValues(
null,
QueryKeyValuesRequest(
uuid: 'uuid',
pagination: PagingRequest(
startKey: 'key-a',
limit: 50.toInt64(),
),
),
)
.then((resp) => {...})
.catchError((err) => {...});
Returns: Future<QueryKeyValuesResponse>
QueryKeyValuesRequest | Description | Type |
---|---|---|
uuid | Database identifier | String |
pagination (optional) | PagingRequest * |
* PagingRequest {startKey: String, limit: Int64}
QueryKeyValuesResponse | Description | Type |
---|---|---|
keyValues | Iterable<KeyValue *> | |
pagination (optional) | PagingResponse ** |
* KeyValue {key: String, value: List<int>}
** PagingResponse {nextKey: String, total: Int64}
Read he complete set of keys by address in the specified uuid.
final myKeysResponse = await sdk.db.q.myKeys(
null,
QueryMyKeysRequest(
uuid: 'uuid',
address: sdk.db.address,
pagination: PagingRequest(
startKey: 'key-a',
limit: 50.toInt64(),
),
),
);
.....
sdk.db.q.myKeys(
null,
QueryMyKeysRequest(
uuid: 'uuid',
address: sdk.db.address,
pagination: PagingRequest(
startKey: 'key-a',
limit: 50.toInt64(),
),
),
)
.then((resp) => {...})
.catchError((err) => {...});
Returns: Future<QueryMyKeysResponse>
QueryMyKeysRequest | Description | Type |
---|---|---|
uuid | Database identifier | String |
address | Bluzelle address | String |
pagination (optional) | PagingRequest * |
*PagingRequest {startKey: String, limit: Int64}
QueryMyKeysResponse | Description | Type |
---|---|---|
keys | Number of key-values in the uuid | Iterable<String> |
pagination (optional) | PagingResponse * |
*PagingResponse {nextKey: String, total: Int64}
Read the complete set of keys by address in the specified uuid.
final readResponse = await sdk.db.q.read(
null,
QueryReadRequest(
uuid: 'uuid',
key: 'existingKey',
),
);
.....
sdk.db.q.read(
null,
QueryReadRequest(
uuid: 'uuid',
key: 'existingKey',
),
)
.then((resp) => {...})
.catchError((err) => {...});
Returns: Future<QueryReadResponse>
QueryReadRequest | Description | Type |
---|---|---|
uuid | Database identifier | String |
key | String |
QueryReadResponse | Description | Type |
---|---|---|
value | List<int> |
Search by key in the specified uuid.
final searchResponse = await sdk.db.q.search(
null,
QuerySearchRequest(
uuid: 'uuid',
searchString: 'keyPrefix',
pagination: PagingRequest(
startKey: 'keyPrefix-A',
limit: 50.toInt64(),
),
),
);
.....
sdk.db.q.search(
null,
QuerySearchRequest(
uuid: 'uuid',
searchString: 'keyPrefix',
pagination: PagingRequest(
startKey: 'keyPrefix-A',
limit: 50.toInt64(),
),
),
)
.then((resp) => {...})
.catchError((err) => {...});
Returns: Future<QuerySearchResponse>
QuerySearchRequest | Description | Type |
---|---|---|
uuid | Database identifier | String |
searchString | Query for keys that start with or match searchString | String |
pagination (optional) | PagingRequest * |
* PagingRequest {startKey: String, limit: Int64}
QuerySearchResponse | Description | Type |
---|---|---|
keyValues | Number of key-values in the uuid | Iterable<KeyValue *> |
pagination (optional) | PagingResponse ** |
* KeyValue {key: String, value: List<int>}
** PagingResponse {nextKey: String, total: Int64}
- To rebuild the generated code (eg. for JSON serialisation):
dart pub run build_runner build
- To download the required Protobuf files and generate their implementations:
./tool/generate_proto.sh
Note: If you want to include new Protobuf files, you can add them to the get_proto.sh.
- To run the tests:
dart pub run test test/
Note: All of the tests are getting run after every commit via github actions.