UNPKG

as-soroban-sdk

Version:

AssemblyScript SDK for writing contracts for Soroban.

280 lines (254 loc) 9.6 kB
import { bytes_append, bytes_back, bytes_copy_from_linear_memory, bytes_copy_to_linear_memory, bytes_del, bytes_front, bytes_get, bytes_insert, bytes_len, bytes_new, bytes_new_from_linear_memory, bytes_pop, bytes_push, bytes_put, bytes_slice, deserialize_from_bytes, serialize_to_bytes } from "./env"; import { BytesObject, Val, fromU32, toU32 } from "./value"; export class Bytes { obj: BytesObject; constructor(obj:BytesObject = bytes_new()) { this.obj = obj; } /** * Returns the handle to the host object as BytesObject. * @returns handle to the host object. */ getHostObject() : BytesObject { return this.obj; } /** * Creates a new Bytes object on the host from the given string. The string must contain only utf8 characters. * @param str the string to create the bytes object from * @returns the Bystes object created. */ static fromString(str: string) : Bytes { let result = new Bytes(); for (let i=0; i < str.length; i++) { result.push(str.charCodeAt(i)); } return result; } /** * Creates a new Bytes object on the host from the given contract id. * If the contract id is shorter than 32 bytes, it fills the needed bytes with 0. * @param contractId contract id as hex string * @returns the new Bytes object created. */ static fromContractId(contractId: string) : Bytes { let result = Bytes.fromHexString(contractId); let len = result.len(); if (len == 32) { return result; } let fill = 32 - len; let filled = new Bytes(); while(fill > 0) { filled.push(0); fill -= 1; } filled = filled.append(result); return filled; } /** * Creates a new Bytes object on the host from the given wasm hash. * If the wasm hash is shorter than 32 bytes, it fills the needed bytes with 0. * @param wasmHash wasm hash as hex string * @returns the new Bytes object created. */ static fromWasmHash(wasmHash: string) : Bytes { let result = Bytes.fromHexString(wasmHash); let len = result.len(); if (len == 32) { return result; } let fill = 32 - len; let filled = new Bytes(); while(fill > 0) { filled.push(0); fill -= 1; } filled = filled.append(result); return filled; } /** * Creates a new Bytes object on the host from the given hex string. * @param hex the hex string to create the bytes object from * @returns the new Bytes object created. */ static fromHexString(hex: string) : Bytes { let result = new Bytes(); let length = hex.length; for (let i = 0; i < (length / 2); i++) { let temp_high = Bytes.charToInt(hex.charCodeAt(i*2)); let temp_low = Bytes.charToInt(hex.charCodeAt(i*2+1)); let app = Bytes.appendNumbers(temp_high, temp_low); result.push(app); } return result; } /** * Creates a new Bytes object by serializing a (SC)Val into XDR opaque `Bytes` object. * @param v value to serialize * @returns the new Bytes object */ static serialize(v:Val) : Bytes { return new Bytes(serialize_to_bytes(v)); } /** * Deserialize this `Bytes` object to get back the (SC)Val. * @returns the deserialized value */ deserialize(): Val { return deserialize_from_bytes(this.obj); } /** * Copies a slice of bytes from this `Bytes` object specified at offset `b_pos` with * length `len` into the linear memory at position `lm_pos`. * Traps if either this `Bytes` object or the linear memory doesn't have enough bytes. * @param b_pos see decription (u32) * @param lm_pos see decription (u32) * @param len see decription (u32) */ copyToLinearMemory(b_pos:u32, lm_pos:u32, len:u32): void { bytes_copy_to_linear_memory(this.obj, fromU32(b_pos), fromU32(lm_pos), fromU32(len)); } /** * Copies a segment of the linear memory specified at position `lm_pos` with * length `len`, into this `Bytes` object at offset `b_pos`. The `Bytes` object may * grow in size to accommodate the new bytes. * Traps if the linear memory doesn't have enough bytes. * @param b_pos see description (u32) * @param lm_pos see description (u32) * @param len see description (u32) * @returns void */ copyFromLinearMemory(b_pos:u32, lm_pos:u32, len:u32): void { this.obj = bytes_copy_from_linear_memory(this.obj, fromU32(b_pos), fromU32(lm_pos), fromU32(len)); } /** * Constructs a new `Bytes` object initialized with bytes copied from a linear memory slice specified at position `lm_pos` with length `len`. * @param lm_pos see desctiption * @param len see desctiption * @returns the new Bytes object. */ static newFromLinearMemory(lm_pos:u32, len:u32): Bytes { return new Bytes(bytes_new_from_linear_memory(fromU32(lm_pos), fromU32(len))); } /** * Update the value at index `i` in the `Bytes` object. * @param i the index to be updated * @param value the value to be updated * @returns void. Traps if the index is out of bound. (e.g. if empty) */ put(i:u32, value: u32) : void { this.obj = bytes_put(this.obj, fromU32(i), fromU32(value)); } /** * Returns the element at index `i` of the `Bytes` object. Traps if the index is out of bound. * @param i the index * @returns the value (u32). Traps if the index is out of bound. */ get(i:u32) : u32 { return toU32(bytes_get(this.obj, fromU32(i))); } /** * Delete an element in this `Bytes` object at index `i`, shifting all elements after it to the left. * @param i the index of the element to be deleted * @returns void. Traps if the index is out of bound. */ del(i:u32) : void { this.obj = bytes_del(this.obj, fromU32(i)); } /** * Returns length of this `Bytes` object. * @returns length of this `Bytes` object. */ len() : u32 { return toU32(bytes_len(this.obj)); } /** * Appends an element to the back of the `Bytes` object. * @param value the value to append (Type: u32) * @returns void. */ push(value: u32) : void { this.obj = bytes_push(this.obj, fromU32(value)); } /** * Removes the last element from the `Bytes` object. * Traps if original `Bytes` is empty. */ pop(): void { this.obj = bytes_pop(this.obj); } /** * Return the first element in the `Bytes` object. Traps if the `Bytes` is empty * @returns the first element in the `Bytes` object (u32). */ front(): u32 { return toU32(bytes_front(this.obj)); } /** * Return the last element in the `Bytes` object. Traps if the `Bytes` is empty * @returns the last element in the `Bytes` object (u32). */ back(): u32 { return toU32(bytes_back(this.obj)); } /** * Inserts an element at index `i` within the `Bytes` object, shifting all elements after it to the right. * Traps if the index is out of bound. * @param i the index to insert the element to * @param value the element to insert (u32). * @returns void */ insert(i: u32, value: u32) : void { this.obj = bytes_insert(this.obj, fromU32(i), fromU32(value)); } /** * Clone this `Bytes` object, then moves all the elements of `Bytes` object `bytes2` into it. * Return the new `Bytes`. Traps if its length overflows a u32. * @param bytes2 the 'Bytes' object to append from * @returns the new `Bytes` object. */ append(bytes2: Bytes) : Bytes { return new Bytes(bytes_append(this.obj, bytes2.getHostObject())); } /** * Copies the elements from `start` index until `end` index, exclusive, in the `Bytes` object and creates a new `Bytes` from it. * @param start see description. * @param end see description. * @returns the new `Bytes` object. */ slice(start:u32, end:u32): Bytes { return new Bytes(bytes_slice(this.obj, fromU32(start), fromU32(end))); } /***************** * HELPERS * ******************/ static charToInt(letter:i32) : i32 { // First we want to check if its 0-9, A-F, or a-f) --> See ASCII Table if(letter > 47 && letter < 58) { // 0-9 return letter - 48; // The Letter "0" is in the ASCII table at position 48 -> meaning if we subtract 48 we get 0 and so on... } else if(letter > 64 && letter < 71) { // A-F return letter - 55 // The Letter "A" (dec 10) is at Pos 65 --> 65-55 = 10 and so on.. } else if(letter > 96 && letter < 103) { // a-f return letter - 87 // The Letter "a" (dec 10) is at Pos 97--> 97-87 = 10 and so on... } // Not supported letter... return -1; } static appendNumbers(higherNibble:i32, lowerNibble:i32) : i32 { let myNumber = higherNibble << 4; myNumber |= lowerNibble; return myNumber; // Example: higherNibble = 0x0A, lowerNibble = 0x03; -> myNumber 0 0xA3 // Of course you have to ensure that the parameters are not bigger than 0x0F } }