@alephium/web3 0.35.1 → 0.36.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -17,9 +17,9 @@ along with the library. If not, see <http://www.gnu.org/licenses/>.
17
17
  */
18
18
 
19
19
  import { Buffer } from 'buffer/'
20
- import { Val, toApiAddress, toApiBoolean, toApiByteVec, toApiNumber256 } from '../api'
21
- import { bs58, isHexString } from '../utils'
22
- import { Fields, FieldsSig } from './contract'
20
+ import { PrimitiveTypes, Val, decodeArrayType, toApiAddress, toApiBoolean, toApiByteVec, toApiNumber256 } from '../api'
21
+ import { binToHex, bs58, isHexString } from '../utils'
22
+ import { Fields, FieldsSig, Struct } from './contract'
23
23
 
24
24
  const bigIntZero = BigInt(0)
25
25
 
@@ -247,47 +247,86 @@ export function encodeScriptField(tpe: string, value: Val): Uint8Array {
247
247
  throw invalidScriptField(tpe, value)
248
248
  }
249
249
 
250
- function flattenArray(name: string, type: string, val: Val[], acc: { name: string; type: string; value: Val }[]) {
251
- const semiColonIndex = type.lastIndexOf(';')
252
- if (semiColonIndex == -1) {
253
- throw new Error(`Invalid array type: ${type}`)
254
- }
255
- const subType = type.slice(1, semiColonIndex)
256
- val.forEach((v, index) => {
257
- const isArrayType = subType.includes(';')
258
- const isArrayValue = Array.isArray(v)
259
- if (isArrayType && isArrayValue) {
260
- flattenArray(`${name}[${index}]`, subType, v, acc)
261
- } else if (!isArrayType && !isArrayValue) {
262
- acc.push({ name: `${name}[${index}]`, type: subType, value: v })
263
- } else {
264
- const value = isArrayValue ? `[` + v.join(', ') + `]` : v.toString()
265
- throw new Error(`Invalid field, expected type is ${subType}, but value is ${value}`)
250
+ export function flattenFields(
251
+ fields: Fields,
252
+ names: string[],
253
+ types: string[],
254
+ isMutable: boolean[],
255
+ structs: Struct[]
256
+ ): { name: string; type: string; value: Val; isMutable: boolean }[] {
257
+ return names.flatMap((name, index) => {
258
+ if (!(name in fields)) {
259
+ throw new Error(`The value of field ${name} is not provided`)
266
260
  }
261
+ return flattenField(isMutable[`${index}`], name, types[`${index}`], fields[`${name}`], structs)
267
262
  })
268
263
  }
269
264
 
270
- export function falttenFields(fields: Fields, fieldsSig: FieldsSig): { name: string; type: string; value: Val }[] {
271
- const allFields: { name: string; type: string; value: Val }[] = []
272
- fieldsSig.names.forEach((name, index) => {
273
- const field = fields[`${name}`]
274
- if (!(name in fields)) {
275
- throw new Error(`The value of field ${name} is not provided`)
265
+ function flattenField(
266
+ isMutable: boolean,
267
+ name: string,
268
+ type: string,
269
+ value: Val,
270
+ structs: Struct[]
271
+ ): { name: string; type: string; value: Val; isMutable: boolean }[] {
272
+ if (Array.isArray(value) && type.startsWith('[')) {
273
+ const [baseType, size] = decodeArrayType(type)
274
+ if (value.length !== size) {
275
+ throw Error(`Invalid array length, expected ${size}, got ${value.length}`)
276
276
  }
277
- const type = fieldsSig.types[`${index}`]
278
- if (Array.isArray(field)) {
279
- flattenArray(name, type, field, allFields)
280
- } else {
281
- allFields.push({ name, type, value: field })
277
+ return value.flatMap((item, index) => {
278
+ return flattenField(isMutable, `${name}[${index}]`, baseType, item, structs)
279
+ })
280
+ }
281
+ const struct = structs.find((s) => s.name === type)
282
+ if (struct !== undefined) {
283
+ if (typeof value !== 'object') {
284
+ throw Error(`Expected an object, but got ${typeof value}`)
282
285
  }
283
- })
284
- return allFields
286
+ return struct.fieldNames.flatMap((fieldName, index) => {
287
+ if (!(fieldName in value)) {
288
+ throw new Error(`The value of field ${fieldName} is not provided`)
289
+ }
290
+ const isFieldMutable = struct.isMutable[`${index}`]
291
+ const fieldType = struct.fieldTypes[`${index}`]
292
+ const fieldValue = value[`${fieldName}`]
293
+ return flattenField(isMutable && isFieldMutable, `${name}.${fieldName}`, fieldType, fieldValue, structs)
294
+ })
295
+ }
296
+ const primitiveType = checkPrimitiveValue(name, type, value)
297
+ return [{ name, type: primitiveType, value, isMutable }]
298
+ }
299
+
300
+ function checkPrimitiveValue(name: string, ralphType: string, value: Val): string {
301
+ const tsType = typeof value
302
+ if (ralphType === 'Bool' && tsType === 'boolean') {
303
+ return ralphType
304
+ }
305
+ if (
306
+ (ralphType === 'U256' || ralphType === 'I256') &&
307
+ (tsType === 'string' || tsType === 'number' || tsType === 'bigint')
308
+ ) {
309
+ return ralphType
310
+ }
311
+ if ((ralphType === 'Address' || ralphType === 'ByteVec') && tsType === 'string') {
312
+ return ralphType
313
+ }
314
+ if (!ralphType.startsWith('[') && tsType === 'string') {
315
+ // contract type
316
+ return 'ByteVec'
317
+ }
318
+ throw Error(`Invalid value ${value} for ${name}, expected a value of type ${ralphType}`)
285
319
  }
286
320
 
287
321
  const scriptFieldRegex = /\{([0-9]*)\}/g
288
322
 
289
- export function buildScriptByteCode(bytecodeTemplate: string, fields: Fields, fieldsSig: FieldsSig): string {
290
- const allFields = falttenFields(fields, fieldsSig)
323
+ export function buildScriptByteCode(
324
+ bytecodeTemplate: string,
325
+ fields: Fields,
326
+ fieldsSig: FieldsSig,
327
+ structs: Struct[]
328
+ ): string {
329
+ const allFields = flattenFields(fields, fieldsSig.names, fieldsSig.types, fieldsSig.isMutable, structs)
291
330
  return bytecodeTemplate.replace(scriptFieldRegex, (_, fieldIndex: string) => {
292
331
  const field = allFields[`${fieldIndex}`]
293
332
  return _encodeField(field.name, () => encodeScriptFieldAsString(field.type, field.value))
@@ -305,27 +344,23 @@ function _encodeField<T>(fieldName: string, encodeFunc: () => T): T {
305
344
  }
306
345
  }
307
346
 
308
- function encodeFields(fields: Fields, fieldsSig: FieldsSig, mutable: boolean) {
309
- const fieldIndexes = fieldsSig.isMutable
310
- .map((_, index) => index)
311
- .filter((index) => fieldsSig.isMutable[`${index}`] === mutable)
312
- const fieldsEncoded = fieldIndexes.flatMap((fieldIndex) => {
313
- const fieldName = fieldsSig.names[`${fieldIndex}`]
314
- const fieldType = fieldsSig.types[`${fieldIndex}`]
315
- if (fieldName in fields) {
316
- const fieldValue = fields[`${fieldName}`]
317
- return _encodeField(fieldName, () => encodeContractField(fieldType, fieldValue))
318
- } else {
319
- throw new Error(`The value of field ${fieldName} is not provided`)
320
- }
321
- })
322
- const fieldsLength = Buffer.from(encodeI256(BigInt(fieldsEncoded.length))).toString('hex')
323
- return fieldsLength + fieldsEncoded.map((f) => Buffer.from(f).toString('hex')).join('')
347
+ function encodeFields(fields: { name: string; type: string; value: Val }[]): string {
348
+ const prefix = binToHex(encodeI256(BigInt(fields.length)))
349
+ const encoded = fields
350
+ .map((field) => binToHex(_encodeField(field.name, () => encodeContractField(field.type, field.value))))
351
+ .join('')
352
+ return prefix + encoded
324
353
  }
325
354
 
326
- export function buildContractByteCode(bytecode: string, fields: Fields, fieldsSig: FieldsSig): string {
327
- const encodedImmFields = encodeFields(fields, fieldsSig, false)
328
- const encodedMutFields = encodeFields(fields, fieldsSig, true)
355
+ export function buildContractByteCode(
356
+ bytecode: string,
357
+ fields: Fields,
358
+ fieldsSig: FieldsSig,
359
+ structs: Struct[]
360
+ ): string {
361
+ const allFields = flattenFields(fields, fieldsSig.names, fieldsSig.types, fieldsSig.isMutable, structs)
362
+ const encodedImmFields = encodeFields(allFields.filter((f) => !f.isMutable))
363
+ const encodedMutFields = encodeFields(allFields.filter((f) => f.isMutable))
329
364
  return bytecode + encodedImmFields + encodedMutFields
330
365
  }
331
366
 
@@ -345,46 +380,25 @@ function encodeContractFieldU256(value: bigint): Uint8Array {
345
380
  return new Uint8Array([ApiValType.U256, ...encodeU256(value)])
346
381
  }
347
382
 
348
- function encodeContractFieldArray(tpe: string, val: Val): Uint8Array[] {
349
- if (!Array.isArray(val)) {
350
- throw new Error(`Expected array, got ${val}`)
351
- }
352
-
353
- const semiColonIndex = tpe.lastIndexOf(';')
354
- if (semiColonIndex == -1) {
355
- throw new Error(`Invalid Array type: ${tpe}`)
356
- }
357
-
358
- const subType = tpe.slice(1, semiColonIndex)
359
- const dim = parseInt(tpe.slice(semiColonIndex + 1, -1))
360
- if ((val as Val[]).length != dim) {
361
- throw new Error(`Invalid val dimension: ${val}`)
362
- } else {
363
- return (val as Val[]).flatMap((v) => encodeContractField(subType, v))
364
- }
365
- }
366
-
367
- export function encodeContractField(tpe: string, value: Val): Uint8Array[] {
383
+ export function encodeContractField(tpe: string, value: Val): Uint8Array {
368
384
  switch (tpe) {
369
385
  case 'Bool':
370
386
  const byte = toApiBoolean(value) ? 1 : 0
371
- return [new Uint8Array([ApiValType.Bool, byte])]
387
+ return new Uint8Array([ApiValType.Bool, byte])
372
388
  case 'I256':
373
389
  const i256 = toApiNumber256(value)
374
- return [encodeContractFieldI256(BigInt(i256))]
390
+ return encodeContractFieldI256(BigInt(i256))
375
391
  case 'U256':
376
392
  const u256 = toApiNumber256(value)
377
- return [encodeContractFieldU256(BigInt(u256))]
393
+ return encodeContractFieldU256(BigInt(u256))
378
394
  case 'ByteVec':
379
395
  const hexStr = toApiByteVec(value)
380
- return [new Uint8Array([ApiValType.ByteVec, ...encodeByteVec(hexStr)])]
396
+ return new Uint8Array([ApiValType.ByteVec, ...encodeByteVec(hexStr)])
381
397
  case 'Address':
382
398
  const address = toApiAddress(value)
383
- return [new Uint8Array([ApiValType.Address, ...encodeAddress(address)])]
384
-
399
+ return new Uint8Array([ApiValType.Address, ...encodeAddress(address)])
385
400
  default:
386
- // Array type
387
- return encodeContractFieldArray(tpe, value)
401
+ throw Error(`Expected primitive type, got ${tpe}`)
388
402
  }
389
403
  }
390
404