Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement CallSmartContract #575

Merged
merged 8 commits into from
Apr 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
717 changes: 717 additions & 0 deletions packages/massa-web3/src/experimental/basicElements/args.ts

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export * from './keys'
export * from './operationManager'
export * from './operation'
export * from './storage'
export * from './args'
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
*
* @remarks
* Period to live is the number of periods the operation is valid for.
* This value must be positive and if it's too big, the node will (sliently?) reject the operation.
* This value must be positive and if it's too big, the node will (silently?) reject the operation.
*
* If no fee is provided, minimal fee of connected node is used.
* If no periodToLive is provided, the DefaultPeriodToLive is used.
Expand All @@ -36,8 +36,8 @@

type BaseOperation = {
fee: bigint
expirePeriod: number
type: OperationType
expirePeriod?: number
}

export type RollOperation = BaseOperation & {
Expand All @@ -59,7 +59,7 @@
export type CallOperation = BaseSmartContractOperation & {
type: OperationType.CallSmartContractFunction
address: string
functionName: string
func: string
parameter: Uint8Array
}

Expand Down Expand Up @@ -110,6 +110,17 @@
operation = operation as RollOperation
components.push(unsigned.encode(operation.amount))
break
case OperationType.CallSmartContractFunction:
// @see https://docs.massa.net/docs/learn/operation-format-execution#callsc-operation-payload
operation = operation as CallOperation
components.push(unsigned.encode(operation.maxGas))
components.push(unsigned.encode(operation.coins))
components.push(Address.fromString(operation.address).toBytes())
components.push(varint.encode(operation.func.length))

Check warning on line 119 in packages/massa-web3/src/experimental/basicElements/operationManager.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement
components.push(Buffer.from(operation.func))

Check warning on line 120 in packages/massa-web3/src/experimental/basicElements/operationManager.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement
components.push(varint.encode(operation.parameter.length))
components.push(operation.parameter)
break

Check warning on line 123 in packages/massa-web3/src/experimental/basicElements/operationManager.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch
case OperationType.ExecuteSmartContractBytecode:
operation = operation as ExecuteOperation
components.push(unsigned.encode(operation.maxGas))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
/* eslint-disable no-case-declarations */

import {
Args,
NativeType,
ArrayTypes,
DeserializedResult,
Serializable,
} from '../args'
import { bytesToStr } from './strings'
import { byteToBool } from './bool'
import {
byteToU8,
bytesToF32,
bytesToF64,
bytesToI32,
bytesToI64,
bytesToU32,
bytesToU64,
} from './numbers'
import { bytesToI128, bytesToU128, bytesToU256 } from './bignum'

/**
* Get the byte size of a typed array unit.
*
* @param typedArrayTypes - The typed array unit to get the size of.
*
* @returns The size of the typed array unit.
*/
export const getDatatypeSize = (type: ArrayTypes): number => {
switch (type) {
case ArrayTypes.BOOL:
case ArrayTypes.U8:
return 1
case ArrayTypes.F32:
case ArrayTypes.I32:
case ArrayTypes.U32:
return 4
case ArrayTypes.F64:
case ArrayTypes.I64:
case ArrayTypes.U64:
return 8
case ArrayTypes.I128:
case ArrayTypes.U128:
return 16
case ArrayTypes.U256:
return 32
Ben-Rey marked this conversation as resolved.
Show resolved Hide resolved
default:
throw new Error(`Unsupported type: ${Object.keys(ArrayTypes)[type]}`)
}
}

/**
* Serializes an array of serializable objects to bytes.
*
* @param source - The array of serializable objects to serialize.
*
* @returns The serialized array as Uint8Array.
*/

export function serializableObjectsArrayToBytes<T extends Serializable<T>>(
source: T[]
): Uint8Array {
return source.reduce(
(acc, curr) => Args.concatArrays(acc, curr.serialize()),
new Uint8Array(0)
)
}

/**
* Deserializes a bytes array into an array of deserialized objects.
Ben-Rey marked this conversation as resolved.
Show resolved Hide resolved
*
* @param data - The bytes array to deserialize.
* @param offset - The offset to start deserializing from.
* @param Obj - The class used for deserialization.
*
* @returns The deserialized array of objects.
*/
export function deserializeObj<T extends Serializable<T>>(
data: Uint8Array,
offset: number,
Obj: new () => T
): DeserializedResult<T> {
return new Obj().deserialize(data, offset)
}

/**
* Converts a Uint8Array into an array of deserialized type parameters.
*
* @param source - The Uint8Array to convert.
* @param Obj - The class constructor for deserialization.
*
* @returns An array of deserialized objects.
*/
export function bytesToSerializableObjectArray<T extends Serializable<T>>(
source: Uint8Array,
Obj: new () => T
): T[] {
const array: T[] = []
let offset = 0

while (offset < source.length) {
let deserializationResult = deserializeObj(source, offset, Obj)
offset = deserializationResult.offset
array.push(deserializationResult.instance)
}

return array
}

/**
* Convert an array of native types to a Uint8Array.
*
* @remarks
* This function performs a deep copy for native types only.
* It is inspired by https://github.com/AssemblyScript/assemblyscript/blob/main/std/assembly/array.ts#L69-L81
*
* @param source - The array to convert.
* @param type - The typed array unit type.
*
* @returns The converted Uint8Array.
*/
export function arrayToBytes(
source: NativeType[],
type: ArrayTypes
): Uint8Array {
let args = new Args()
source.forEach((value) => {
switch (type) {
case ArrayTypes.STRING:
args.addString(value as string)
break
case ArrayTypes.BOOL:
args.addBool(value as boolean)
break
case ArrayTypes.U8:
args.addU8(value as number)
break
case ArrayTypes.F64:
args.addF64(value as number)
break
case ArrayTypes.F32:
args.addF32(value as number)
break
case ArrayTypes.I32:
args.addI32(value as number)
break
case ArrayTypes.I64:
args.addI64(value as bigint)
break
case ArrayTypes.U32:
args.addU32(value as number)
break
case ArrayTypes.U64:
args.addU64(value as bigint)
break
case ArrayTypes.I128:
args.addI128(value as bigint)
break
case ArrayTypes.U128:
args.addU128(value as bigint)
break
case ArrayTypes.U256:
args.addU256(value as bigint)
break
default:
throw new Error(`Unsupported type: ${type}`)
}
})
return new Uint8Array(args.serialize())
}

/**
* Converts a Uint8Array into an array of native types.
*
* @remarks
* This function is inspired by https://github.com/AssemblyScript/assemblyscript/blob/main/std/assembly/array.ts#L69-L81
*
* @param source - The Uint8Array to convert.
* @param type - The typed array unit type.
*
* @returns An array of converted native types.
*/
export function bytesToArray<T>(source: Uint8Array, type: ArrayTypes): T[] {
const sourceLength = source.length

let byteOffset = 0
const result: T[] = []
let eltSize: number

if (type !== ArrayTypes.STRING) {
eltSize = getDatatypeSize(type)
}

while (byteOffset < sourceLength) {
if (type === ArrayTypes.STRING) {
eltSize = bytesToU32(source, byteOffset)
byteOffset += 4
}
const elt = source.slice(byteOffset, byteOffset + eltSize)
byteOffset += eltSize

switch (type) {
case ArrayTypes.STRING:
result.push(bytesToStr(elt) as T)
break
case ArrayTypes.BOOL:
result.push(byteToBool(elt) as T)
break
case ArrayTypes.U8:
result.push(byteToU8(elt) as T)
break
case ArrayTypes.F32:
result.push(bytesToF32(elt) as T)
break
case ArrayTypes.F64:
result.push(bytesToF64(elt) as T)
break
case ArrayTypes.I32:
result.push(bytesToI32(elt) as T)
break
case ArrayTypes.I64:
result.push(bytesToI64(elt) as T)
break
case ArrayTypes.U32:
result.push(bytesToU32(elt) as T)
break
case ArrayTypes.U64:
result.push(bytesToU64(elt) as T)
break
case ArrayTypes.I128:
result.push(bytesToI128(elt) as T)
break
case ArrayTypes.U128:
result.push(bytesToU128(elt) as T)
break
case ArrayTypes.U256:
result.push(bytesToU256(elt) as T)
break
}
}
return result
}
Loading
Loading