I want my API to return an array of uint64 to my on-chain contract.
I tried 2 response formats for my API:
The array of uint64 itself (BN string here, but I need it in true uint64 not strings in my contract):
{"data":["629343835796877311","629343835797458943","629343835797471231"]}
concatenated hexadecimal strings (a new value every 16 chars):
{"data":"08bbe0e25e412fff08bbe0e25e4a0fff08bbe0e25e4a3fff"}
I discarded using the first approach because having ["629343835796877311","629343835797458943","629343835797471231"] as bytes is actually difficult to extract. I might be wrong! Maybe there is a base64 approach to encode and decode the data back into solidity data types, maybe?
I will use the second approach bellow.
Chainlink will pass the response as bytes memory _data:
function fulfill(bytes32 _requestId, bytes memory _data)
public
recordChainlinkFulfillment(_requestId)
{
data = string(_data);
}
Those bytes memory _data are successfully received and converted to a string (in storage data). The string value looks like this
08bbe0e25e412fff08bbe0e25e4a0fff08bbe0e25e4a3fff ...
In this example each 16 chars represent a uint64 number.
The first one: 08bbe0e25e412fff is 629343835796877311 for instance.
In solidity, I need to split the string each 16 chars and then convert it into their uint64 value.
I could use the bytes memory _data instead of the string(_data) if the code would be simpler or consume less gas. I am not sure
Please I need help with this I have been struggling.
Thanks
I got this contract working
COMMENTS:
the method hexBytesToInt is going to get a string representing hexa value like ffa0 for instance and return it's decimal value.
the method getSlice is just going to slice a string. In my case I have a new hexa value every 16 chars so I need to slice (0,16) than (16,32) etc...
the method hexStringToIntArray is managing the increments to slice every 16 chars and call the hexBytesToInt to transform the hex string in uint.
If you really want to dig into this solution, you are better off starting by understanding the test cases.
pragma solidity >=0.4.22 <0.8.11;
contract Serializer {
function hexStringToIntArray(string memory s) public pure returns (uint64[] memory) {
uint size = bytes(s).length / 16;
uint64[] memory result = new uint64[](size);
for (uint i = 0; i< size; i++) {
string memory strSlice = getSlice(i*16, (i+1)*16, s);
result[i] = hexStringToInt(strSlice);
}
return result;
}
function getSlice(uint startIndex, uint endIndex, string memory str) public pure returns (string memory) {
bytes memory strBytes = bytes(str);
bytes memory result = new bytes(endIndex-startIndex);
for(uint i = startIndex; i < endIndex; i++) {
result[i-startIndex] = strBytes[i];
}
return string(result);
}
function hexBytesToInt(bytes memory ss) public pure returns (uint64){
uint64 val = 0;
uint8 a = uint8(97); // a
uint8 zero = uint8(48); //0
uint8 nine = uint8(57); //9
uint8 A = uint8(65); //A
uint8 F = uint8(70); //F
uint8 f = uint8(102); //f
for (uint i=0; i<ss.length; ++i) {
uint8 byt = uint8(ss[i]);
if (byt >= zero && byt <= nine) byt = byt - zero;
else if (byt >= a && byt <= f) byt = byt - a + 10;
else if (byt >= A && byt <= F) byt = byt - A + 10;
val = (val << 4) | (byt & 0xF);
}
return val;
}
function hexStringToInt(string memory s) public pure returns (uint64) {
bytes memory ss = bytes(s);
uint64 val = hexBytesToInt(ss);
return val;
}
}
the tests:
const Serializer = artifacts.require("Serializer");
const truffleAssert = require("truffle-assertions");
const fs = require("fs");
const { readLines } = require("./utils.js");
const BN = web3.utils.BN;
contract("Serializer", (accounts) => {
const [deployerAddress, tokenHolderOneAddress, tokenHolderTwoAddress] = accounts;
it("hexStringToInt", async () => {
let s = await Serializer.deployed();
let result = await s.hexStringToInt.call("08bbe0e25e412fff");
let expected = new BN("629343835796877311");
assert.equal(result.toString(10), expected.toString(10));
result = await s.hexStringToInt.call("08bbe0e25e4a0fff");
expected = new BN("629343835797458943");
assert.equal(result.toString(10), expected.toString(10));
result = await s.hexStringToInt.call("08bbe0e25e4a3fff");
expected = new BN("629343835797471231");
assert.equal(result.toString(10), expected.toString(10));
});
it("getSlice1", async () => {
let s = await Serializer.deployed();
let result = await s.getSlice.call(0, 16, "08bbe0e25e412fff08bbe0e25e4a0fff08bbe0e25e4a3fff");
let expected = "08bbe0e25e412fff";
assert.equal(result, expected);
});
it("getSlice2", async () => {
let s = await Serializer.deployed();
const result = await s.getSlice.call(16, 32, "08bbe0e25e412fff08bbe0e25e4a0fff08bbe0e25e4a3fff");
const expected = "08bbe0e25e4a0fff";
assert.equal(result, expected);
});
it("getSlice3", async () => {
let s = await Serializer.deployed();
const result = await s.getSlice.call(32, 48, "08bbe0e25e412fff08bbe0e25e4a0fff08bbe0e25e4a3fff");
const expected = "08bbe0e25e4a3fff";
assert.equal(result, expected);
});
it("hexStringToIntArray", async () => {
let s = await Serializer.deployed();
let result = await s.hexStringToIntArray.call("08bbe0e25e412fff08bbe0e25e4a0fff08bbe0e25e4a3fff");
console.log(result);
let expected = [
new BN("629343835796877311").toString(),
new BN("629343835797458943").toString(),
new BN("629343835797471231").toString(),
];
const resultS = result.map((x) => x.toString());
assert.deepEqual(resultS, expected);
});
});
Related
There is a string with random numbers and letters. I need to divide this string into 5 parts. And get List. How to do it? Thanks.
String str = '05b37ffe4973959c4d4f2d5ca0c1435749f8cc66';
Should work:
List<String> list = [
'05b37ffe',
'4973959c',
'4d4f2d5c',
'a0c14357',
'49f8cc66',
];
I know there'a already a working answer but I had already started this so here's a different solution.
String str = '05b37ffe4973959c4d4f2d5ca0c1435749f8cc66';
List<String> list = [];
final divisionIndex = str.length ~/ 5;
for (int i = 0; i < str.length; i++) {
if (i % divisionIndex == 0) {
final tempString = str.substring(i, i + divisionIndex);
list.add(tempString);
}
}
log(list.toString()); // [05b37ffe, 4973959c, 4d4f2d5c, a0c14357, 49f8cc66]
String str = '05b37ffe4973959c4d4f2d5ca0c1435749f8cc66';
int d=1
; try{
d = (str.length/5).toInt();
print(d);
}catch(e){
d=1;
}
List datas=[];
for(int i=0;i<d;i++){
var c=i+1;
try {
datas.add(str.substring(i * d, d*c));
} catch (e) {
print(e);
}
}
print(datas);
}
OR
String str = '05b37ffe4973959c4d4f2d5ca0c1435749f8cc66';
int d = (str.length / 5).toInt();
var data = List.generate(d - 3, (i) => (d * (i + 1)) <= str.length ? str.substring(i * d, d * (i + 1)) : "");
print(data);//[05b37ffe, 4973959c, 4d4f2d5c, a0c14357, 49f8cc66]
If you're into one liners, with dynamic parts.
Make sure to import dart:math for min function.
This is modular, i.e. you can pass whichever number of parts you want (default 5). If you string is 3 char long, and you want 5 parts, then it'll return 3 parts with 1 char in each.
List<String> splitIntoEqualParts(String str, [int parts = 5]) {
int _parts = min(str.length, parts);
int _sublength = (str.length / _parts).ceil();
return Iterable<int>
//Initialize empty list
.generate(_parts)
.toList()
// Apply the access logic
.map((index) => str.substring(_sublength * index, min(_sublength * index + _sublength, str.length)))
.toList();
}
You can then use it such as print(splitIntoEqualParts('05b37ffe4973959c4d4f2d5ca0c1435749f8cc66', 5));
splitWithCount(String string,int splitCount)
{
var array = [];
for(var i =0 ;i<=(string.length-splitCount);i+=splitCount)
{
var start = i;
var temp = string.substring(start,start+splitCount);
array.add(temp);
}
print(array);
}
I have a csv dataset which contains numerical data.
eg:
76.21203492,30.86714946,0
76.23332579,30.86005251,1
76.14016701,30.85789648,2
I am reading this data using the following function:
loadAsset() async {
final myData = await rootBundle
.loadString("assets/data.csv");
List<List<dynamic>> csvTable = CsvToListConverter().convert(myData);
data = csvTable;
}
Now, I want to read the values.
Future<void> _onMapCreated(GoogleMapController controller) async {
await loadAsset();
setState(() {
_markers.clear();
int k = 0;
for (final loc in data) {
final marker = Marker(
markerId: MarkerId(loc[2]),
position: LatLng(double.parse(loc[0]), double.parse(loc[1])),
infoWindow: InfoWindow(
title: loc[2],
snippet: loc[2],
),
);
_markers[k.toString()] = marker;
k++;
}
});
But I am unable to do so.
It gives an error in line
LatLng(double.parse(loc[0]), double.parse(loc[1]))
quoting:
type 'double' is not a subtype of type 'String'
I also tried using the following function:
double convertion(String number) {
double value = 0;
int i = 0;
List<int> list = number.codeUnits.toList();
String zero = '0';
List<int> zeros = zero.codeUnits.toList();
while (number[i] != '.') {
value *= 10;
value += list[i].toDouble() - zeros[0].toDouble();
i++;
}
i++;
double decimal = 0;
int k = 0;
while (i < list.length) {
k++;
decimal *= 10;
decimal += list[i].toDouble() - zeros[0].toDouble();
i++;
}
decimal *= (pow(10, (k * (-1))));
value += decimal;
return value;
}
But I get the same error.
CsvToListConverter automatically parses all ints and doubles into the correct type, so double.parse(loc[0]) gives you an error because loc[0] is already a double, and you obviously can't parse a double from a double.
To fix this, change the line as follows:
From:
LatLng(double.parse(loc[0]), double.parse(loc[1]))
To:
LatLng(loc[0], loc[1])
When i read data from a big txt file block by block ,I got the error as blow:
Unfinished UTF-8 octet sequence (at offset 4096)
code:
File file = File(path!);
RandomAccessFile _raf = await file.open();
_raf.setPositionSync(skip ?? 0);
var data = _raf.readSync(block);// block = 64*64
content.value = utf8.decode(data.toList());
UTF*8 is variable length encoding.
The error come from data not align to UTF8 boundary
Alternative way is to trim data byte on left and right before call utf.decode
This will lost first and last character. You may read and add more bytes to cover last character and align with utf8 boundary
bool isDataByte(int i) {
return i & 0xc0 == 0x80;
}
Future<void> main(List<String> arguments) async {
var _raf = await File('utf8.txt').open();
_raf.setPositionSync(skip);
var data = _raf.readSync(8 * 8);
var utfData = data.toList();
int l, r;
for (l = 0; isDataByte(utfData[l]) && l < utfData.length; l++) {}
for (r = utfData.length - 1; isDataByte(utfData[r]) && r > l; r--) {}
var value = utf8.decode(utfData.sublist(l, r));
print(value);
}
Optional read more 4 bytes and expand to cover last character
bool isDataByte(int i) {
return i & 0xc0 == 0x80;
}
Future<void> main(List<String> arguments) async {
var _raf = await File('utf8.txt').open();
_raf.setPositionSync(skip);
var block = 8 * 8;
var data = _raf.readSync(block + 4);
var utfData = data.toList();
int l, r;
for (l = 0; isDataByte(utfData[l]) && l < block; l++) {}
for (r = block; isDataByte(utfData[r]) && r < block + 4; r++) {}
var value = utf8.decode(utfData.sublist(l, r));
print(value);
}
I'm totally new at app developing.
I trying to communicate with C-written end device via raw UDP packet. (such a modbus-like protocol)
And I'm suffering pain with serial/deserializing class(struct).
Here is a simple class, Packet which contain uint32, uint16, uint8.
Pack() is working, but are there any better way to achieve that?
I don't know how can I implement Unpack() method. I mean, how can I convert Uint8List to int?
import 'dart:typed_data';
void main() {
var pkt = new Packet();
pkt.TID = 0x01234567;
pkt.Src = 0x89;
pkt.Des = 0xab;
pkt.Data = 0xcdef;
var buf = pkt.Pack();
print('Packed: ${buf}');
var pkt_2 = new Packet();
if (pkt_2.Unpack(buf) != null) {
print("panic!");
return;
}
print('UnPacked: ${pkt_2.toString()}');
}
class Packet {
int TID; // uint32
int Src; // uint8
int Des; // uint8
int Data; // uint16
static const SIZE = 8;
String toString() {
return 'TID: ${TID.toRadixString(16)}, Src:${Src.toRadixString(16)}, Des:${Des.toRadixString(16)}, Data:${Data.toRadixString(16)}';
}
Uint8List Pack() {
var buf = Byteconv.itou32(TID) +
Byteconv.itou8(Src) +
Byteconv.itou8(Des) +
Byteconv.itou16(Data);
return Uint8List.fromList(buf);
}
Error Unpack(Uint8List buf) {
if (buf.length != SIZE) {
return Error();
}
// What can I do?
// TID =
// Src =
// Des =
// Data =
return null;
}
}
class Byteconv {
static Uint8List itou64(int val) {
return Uint8List(8)..buffer.asByteData().setUint64(0, val, Endian.big);
}
static Uint8List itou32(int val) {
return Uint8List(4)..buffer.asByteData().setUint32(0, val, Endian.big);
}
static Uint8List itou16(int val) {
return Uint8List(2)..buffer.asByteData().setUint16(0, val, Endian.big);
}
static Uint8List itou8(int u8) {
return Uint8List(1)..buffer.asUint8List()[0] = u8;
}
}
SOLVED
var buf_view = ByteData.sublistView(buf);
TID = buf_view.getUint32(0);
Src = buf_view.getUint8(4);
Des = buf_view.getUint8(5);
Data = buf_view.getUint16(6);
I need to convert a string of 8-character hexadecimal substrings into a list of integers.
For example, I might have the string
001479B70054DB6E001475B3
which consists of the following substrings
001479B7 // 1341879 decimal
0054DB6E // 5561198 decimal
001475B3 // 1340851 decimal
I'm currently using convert.hex to first convert the strings into a list of 4 integers (because convert.hex only handles parsing 2-character hex strings) and then adding/multiplying those up:
String tmp;
for(int i=0; i<=myHexString.length-8; i+=8){
tmp = myHexString.substring(i, i+8);
List<int> ints = hex.decode(tmp);
int dec = ints[3]+(ints[2]*256+(ints[1]*65536)+(ints[0]*16777216));
}
Is there a more efficient way to do this?
You can use int.parse('001479B7', radix: 16);
https://api.dartlang.org/stable/2.4.1/dart-core/int/parse.html
so your code will look like this :
void main() {
final fullString = '001479B70054DB6E001475B3';
for (int i = 0; i <= fullString.length - 8; i += 8) {
final hex = fullString.substring(i, i + 8);
final number = int.parse(hex, radix: 16);
print(number);
}
}
Since my Hex string came smaller than 8 elements of Byte, I did this.
String dumpHexToString(List<int> data) {
StringBuffer sb = StringBuffer();
data.forEach((f) {
sb.write(f.toRadixString(16).padLeft(2, '0'));
sb.write(" ");
});
return sb.toString();
}
String conertHexDecimal(String str1) {
final fullString = str1;
int number = 0;
for (int i = 0; i <= fullString.length - 8; i += 8) {
final hex = fullString.substring(i, i + 8);
number = int.parse(hex, radix: 16);
print(number);
}
return number.toString();
}
void executarConersao(Uint8List data){
String conersorHexDeVar = dumpHexToString(data);
conersorHexDeVar = conersorHexDeVar
.substring(3, conersorHexDeVar.length)
.replaceAll(' ', '')
.padLeft(8, '0');
conersorHexDeVar = conertHexDecimal(conersorHexDeVar);
print('data $conersorHexDeVar');
}
For anyone who wants to convert hexadecimal numbers to 2's component, Dart / Flutter has a builtin method - .toSigned(int):
var testConversion = 0xC1.toSigned(8);
print("This is the result: " + testConversion.toString()); // prints -63