@alephium/web3 0.39.3 → 0.41.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.
Files changed (64) hide show
  1. package/dist/alephium-web3.min.js +1 -1
  2. package/dist/alephium-web3.min.js.map +1 -1
  3. package/dist/src/api/api-alephium.d.ts +1 -1
  4. package/dist/src/api/api-alephium.js +1 -1
  5. package/dist/src/api/node-provider.d.ts +2 -0
  6. package/dist/src/api/node-provider.js +12 -6
  7. package/dist/src/api/utils.d.ts +1 -1
  8. package/dist/src/block/block.d.ts +28 -0
  9. package/dist/src/block/block.js +131 -0
  10. package/dist/src/block/index.d.ts +1 -0
  11. package/dist/src/block/index.js +22 -0
  12. package/dist/src/codec/contract-output-codec.js +4 -4
  13. package/dist/src/codec/instr-codec.d.ts +3 -0
  14. package/dist/src/codec/instr-codec.js +19 -3
  15. package/dist/src/codec/lockup-script-codec.js +2 -2
  16. package/dist/src/codec/method-codec.d.ts +3 -1
  17. package/dist/src/codec/method-codec.js +27 -2
  18. package/dist/src/codec/script-codec.d.ts +11 -6
  19. package/dist/src/codec/script-codec.js +13 -2
  20. package/dist/src/codec/transaction-codec.js +2 -2
  21. package/dist/src/codec/unlock-script-codec.d.ts +2 -2
  22. package/dist/src/codec/unsigned-tx-codec.d.ts +2 -2
  23. package/dist/src/contract/contract.d.ts +25 -7
  24. package/dist/src/contract/contract.js +136 -51
  25. package/dist/src/contract/events.d.ts +1 -2
  26. package/dist/src/contract/events.js +28 -14
  27. package/dist/src/index.d.ts +1 -0
  28. package/dist/src/index.js +1 -0
  29. package/dist/src/signer/tx-builder.js +4 -4
  30. package/dist/src/transaction/status.js +28 -4
  31. package/dist/src/utils/address.js +29 -16
  32. package/dist/src/utils/exchange.js +25 -15
  33. package/dist/src/utils/number.d.ts +1 -1
  34. package/dist/src/utils/sign.js +6 -6
  35. package/dist/src/utils/subscription.d.ts +4 -4
  36. package/dist/src/utils/subscription.js +1 -1
  37. package/package.json +5 -5
  38. package/src/api/api-alephium.ts +1 -1
  39. package/src/api/node-provider.ts +8 -1
  40. package/src/api/utils.ts +1 -1
  41. package/src/block/block.ts +139 -0
  42. package/src/block/index.ts +19 -0
  43. package/src/codec/contract-output-codec.ts +1 -1
  44. package/src/codec/instr-codec.ts +14 -1
  45. package/src/codec/lockup-script-codec.ts +3 -3
  46. package/src/codec/method-codec.ts +41 -3
  47. package/src/codec/script-codec.ts +23 -5
  48. package/src/codec/transaction-codec.ts +1 -1
  49. package/src/codec/unlock-script-codec.ts +2 -2
  50. package/src/codec/unsigned-tx-codec.ts +2 -2
  51. package/src/contract/contract.ts +178 -67
  52. package/src/contract/events.ts +6 -18
  53. package/src/index.ts +1 -0
  54. package/src/signer/tx-builder.ts +2 -2
  55. package/src/transaction/status.ts +4 -4
  56. package/src/utils/address.ts +15 -2
  57. package/src/utils/exchange.ts +32 -10
  58. package/src/utils/number.ts +1 -1
  59. package/src/utils/sign.ts +1 -1
  60. package/src/utils/subscription.ts +4 -4
  61. package/std/fungible_token_interface.ral +1 -0
  62. package/std/nft_collection_interface.ral +1 -0
  63. package/std/nft_collection_with_royalty_interface.ral +1 -0
  64. package/std/nft_interface.ral +1 -0
@@ -15,30 +15,48 @@ GNU Lesser General Public License for more details.
15
15
  You should have received a copy of the GNU Lesser General Public License
16
16
  along with the library. If not, see <http://www.gnu.org/licenses/>.
17
17
  */
18
+
18
19
  import { Buffer } from 'buffer/'
19
20
  import { Parser } from 'binary-parser'
20
21
  import { DecodedArray } from './array-codec'
21
22
  import { Codec } from './codec'
22
- import { DecodedMethod, methodsCodec } from './method-codec'
23
+ import { DecodedMethod, methodsCodec, Method, MethodCodec } from './method-codec'
23
24
  import { OptionCodec } from './option-codec'
25
+ import { compactUnsignedIntCodec } from './compact-int-codec'
24
26
 
25
- export interface Script {
27
+ export interface DecodedScript {
26
28
  methods: DecodedArray<DecodedMethod>
27
29
  }
28
30
 
29
- export class ScriptCodec implements Codec<Script> {
31
+ export interface Script {
32
+ methods: Method[]
33
+ }
34
+
35
+ export class ScriptCodec implements Codec<DecodedScript> {
30
36
  parser = Parser.start().nest('methods', {
31
37
  type: methodsCodec.parser
32
38
  })
33
39
 
34
- encode(input: Script): Buffer {
40
+ encode(input: DecodedScript): Buffer {
35
41
  const script = methodsCodec.encode(input.methods.value)
36
42
  return Buffer.from(script)
37
43
  }
38
44
 
39
- decode(input: Buffer): Script {
45
+ decode(input: Buffer): DecodedScript {
40
46
  return this.parser.parse(input)
41
47
  }
48
+
49
+ decodeScript(input: Buffer): Script {
50
+ const decodedTxScript = this.decode(input)
51
+ const methods = decodedTxScript.methods.value.map((decodedMethod) => MethodCodec.toMethod(decodedMethod))
52
+ return { methods }
53
+ }
54
+
55
+ encodeScript(inputTxScript: Script): Buffer {
56
+ const methodLength = compactUnsignedIntCodec.fromU32(inputTxScript.methods.length)
57
+ const decodedMethods = inputTxScript.methods.map((method) => MethodCodec.fromMethod(method))
58
+ return this.encode({ methods: { value: decodedMethods, length: methodLength } })
59
+ }
42
60
  }
43
61
 
44
62
  export const scriptCodec = new ScriptCodec()
@@ -27,7 +27,7 @@ import { Either } from './either-codec'
27
27
  import { AssetOutput, AssetOutputCodec } from './asset-output-codec'
28
28
  import { ContractOutput, ContractOutputCodec } from './contract-output-codec'
29
29
  import { FixedAssetOutput, Transaction as ApiTransaction } from '../api/api-alephium'
30
- import { hexToBinUnsafe } from '..'
30
+ import { hexToBinUnsafe } from '../utils'
31
31
  import { ContractOutput as ApiContractOutput } from '../api/api-alephium'
32
32
  import { Codec } from './codec'
33
33
  import { Output, outputCodec, outputsCodec } from './output-codec'
@@ -20,7 +20,7 @@ import { Parser } from 'binary-parser'
20
20
  import { ArrayCodec, DecodedArray } from './array-codec'
21
21
  import { compactUnsignedIntCodec, compactSignedIntCodec, DecodedCompactInt } from './compact-int-codec'
22
22
  import { Codec } from './codec'
23
- import { Script, scriptCodec } from './script-codec'
23
+ import { DecodedScript, Script, scriptCodec } from './script-codec'
24
24
  import { ByteString, byteStringCodec } from './bytestring-codec'
25
25
  import { LockupScript, lockupScriptCodec } from './lockup-script-codec'
26
26
 
@@ -122,7 +122,7 @@ const valCodec = new ValCodec()
122
122
  const valsCodec = new ArrayCodec(valCodec)
123
123
 
124
124
  export interface P2SH {
125
- script: Script
125
+ script: DecodedScript
126
126
  params: DecodedArray<Val>
127
127
  }
128
128
 
@@ -19,7 +19,7 @@ import { Buffer } from 'buffer/'
19
19
  import { Parser } from 'binary-parser'
20
20
  import { UnsignedTx as ApiUnsignedTx } from '../api/api-alephium'
21
21
  import { binToHex, hexToBinUnsafe } from '../utils'
22
- import { Script, scriptCodec, statefulScriptCodecOpt } from './script-codec'
22
+ import { DecodedScript, scriptCodec, statefulScriptCodecOpt } from './script-codec'
23
23
  import { Option } from './option-codec'
24
24
  import { DecodedCompactInt, compactSignedIntCodec, compactUnsignedIntCodec } from './compact-int-codec'
25
25
  import { Input, InputCodec, inputsCodec } from './input-codec'
@@ -31,7 +31,7 @@ import { Codec } from './codec'
31
31
  export interface UnsignedTx {
32
32
  version: number
33
33
  networkId: number
34
- statefulScript: Option<Script>
34
+ statefulScript: Option<DecodedScript>
35
35
  gasAmount: DecodedCompactInt
36
36
  gasPrice: DecodedCompactInt
37
37
  inputs: DecodedArray<Input>
@@ -34,7 +34,8 @@ import {
34
34
  getDefaultPrimitiveValue,
35
35
  PrimitiveTypes,
36
36
  decodeArrayType,
37
- fromApiPrimitiveVal
37
+ fromApiPrimitiveVal,
38
+ tryGetCallResult
38
39
  } from '../api'
39
40
  import { CompileProjectResult } from '../api/api-alephium'
40
41
  import {
@@ -87,7 +88,7 @@ const crypto = new WebCrypto()
87
88
  export type FieldsSig = node.FieldsSig
88
89
  export type MapsSig = node.MapsSig
89
90
  export type EventSig = node.EventSig
90
- export type FunctionSig = node.FunctionSig
91
+ export type FunctionSig = Omit<node.FunctionSig, 'isPublic' | 'usePreapprovedAssets' | 'useAssetsInContract'>
91
92
  export type Fields = NamedVals
92
93
  export type Arguments = NamedVals
93
94
  export type Constant = node.Constant
@@ -280,7 +281,18 @@ export class ProjectArtifact {
280
281
  return fsPromises.writeFile(filepath, content)
281
282
  }
282
283
 
283
- needToReCompile(compilerOptions: node.CompilerOptions, sourceInfos: SourceInfo[], fullNodeVersion: string): boolean {
284
+ getChangedSources(sourceInfos: SourceInfo[]): SourceInfo[] {
285
+ const result: SourceInfo[] = []
286
+ for (const sourceInfo of sourceInfos) {
287
+ const info = this.infos.get(sourceInfo.name)
288
+ if (typeof info === 'undefined' || info.sourceCodeHash !== sourceInfo.sourceCodeHash) {
289
+ result.push(sourceInfo)
290
+ }
291
+ }
292
+ return result
293
+ }
294
+
295
+ needToReCompile(compilerOptions: node.CompilerOptions, fullNodeVersion: string): boolean {
284
296
  ProjectArtifact.checkCompilerOptionsParameter(compilerOptions)
285
297
  if (this.fullNodeVersion !== fullNodeVersion) {
286
298
  return true
@@ -294,16 +306,6 @@ export class ProjectArtifact {
294
306
  return true
295
307
  }
296
308
 
297
- if (sourceInfos.length !== this.infos.size) {
298
- return true
299
- }
300
- for (const sourceInfo of sourceInfos) {
301
- const info = this.infos.get(sourceInfo.name)
302
- if (typeof info === 'undefined' || info.sourceCodeHash !== sourceInfo.sourceCodeHash) {
303
- return true
304
- }
305
- }
306
-
307
309
  return false
308
310
  }
309
311
 
@@ -326,15 +328,22 @@ export class ProjectArtifact {
326
328
  }
327
329
  }
328
330
 
329
- function removeOldArtifacts(dir: string) {
331
+ function removeOldArtifacts(dir: string, sourceFiles: SourceInfo[]) {
330
332
  const files = fs.readdirSync(dir)
331
333
  files.forEach((file) => {
332
334
  const filePath = path.join(dir, file)
333
335
  const stat = fs.statSync(filePath)
334
336
  if (stat.isDirectory()) {
335
- removeOldArtifacts(filePath)
337
+ removeOldArtifacts(filePath, sourceFiles)
336
338
  } else if (filePath.endsWith('.ral.json') || filePath.endsWith('.ral')) {
337
- fs.unlinkSync(filePath)
339
+ const filename = path.basename(filePath)
340
+ const artifactName = filename.slice(0, filename.indexOf('.'))
341
+ const sourceFile = sourceFiles.find(
342
+ (s) => s.name === artifactName && (s.type === SourceKind.Contract || s.type === SourceKind.Script)
343
+ )
344
+ if (sourceFile === undefined) {
345
+ fs.unlinkSync(filePath)
346
+ }
338
347
  }
339
348
  })
340
349
 
@@ -521,7 +530,11 @@ export class Project {
521
530
  return fsPromises.writeFile(filePath, JSON.stringify(structs, null, 2))
522
531
  }
523
532
 
524
- private async saveArtifactsToFile(projectRootDir: string): Promise<void> {
533
+ private async saveArtifactsToFile(
534
+ projectRootDir: string,
535
+ skipSaveArtifacts: boolean,
536
+ changedSources: SourceInfo[]
537
+ ): Promise<void> {
525
538
  const artifactsRootDir = this.artifactsRootDir
526
539
  const saveToFile = async function (compiled: Compiled<Artifact>): Promise<void> {
527
540
  const artifactPath = compiled.sourceInfo.getArtifactPath(artifactsRootDir)
@@ -531,9 +544,32 @@ export class Project {
531
544
  }
532
545
  return fsPromises.writeFile(artifactPath, compiled.artifact.toString())
533
546
  }
534
- this.contracts.forEach((contract) => saveToFile(contract))
535
- this.scripts.forEach((script) => saveToFile(script))
536
- this.saveStructsToFile()
547
+ for (const [_, contract] of this.contracts) {
548
+ if (!skipSaveArtifacts || changedSources.find((s) => s.name === contract.sourceInfo.name) !== undefined) {
549
+ await saveToFile(contract)
550
+ }
551
+ }
552
+ for (const [_, script] of this.scripts) {
553
+ await saveToFile(script)
554
+ }
555
+ await this.saveStructsToFile()
556
+ await this.saveProjectArtifact(projectRootDir, skipSaveArtifacts, changedSources)
557
+ }
558
+
559
+ private async saveProjectArtifact(projectRootDir: string, skipSaveArtifacts: boolean, changedSources: SourceInfo[]) {
560
+ if (skipSaveArtifacts) {
561
+ // we should not update the `codeHashDebug` if the `skipSaveArtifacts` is enabled
562
+ const prevProjectArtifact = await ProjectArtifact.from(projectRootDir)
563
+ if (prevProjectArtifact !== undefined) {
564
+ for (const [name, info] of this.projectArtifact.infos) {
565
+ if (changedSources.find((s) => s.name === name) === undefined) {
566
+ const prevInfo = prevProjectArtifact.infos.get(name)
567
+ info.bytecodeDebugPatch = prevInfo?.bytecodeDebugPatch ?? info.bytecodeDebugPatch
568
+ info.codeHashDebug = prevInfo?.codeHashDebug ?? info.codeHashDebug
569
+ }
570
+ }
571
+ }
572
+ }
537
573
  await this.projectArtifact.saveToFile(projectRootDir)
538
574
  }
539
575
 
@@ -587,7 +623,9 @@ export class Project {
587
623
  contractsRootDir: string,
588
624
  artifactsRootDir: string,
589
625
  errorOnWarnings: boolean,
590
- compilerOptions: node.CompilerOptions
626
+ compilerOptions: node.CompilerOptions,
627
+ changedSources: SourceInfo[],
628
+ skipSaveArtifacts = false
591
629
  ): Promise<Project> {
592
630
  const removeDuplicates = sourceInfos.reduce((acc: SourceInfo[], sourceInfo: SourceInfo) => {
593
631
  if (acc.find((info) => info.sourceCodeHash === sourceInfo.sourceCodeHash) === undefined) {
@@ -639,20 +677,23 @@ export class Project {
639
677
  errorOnWarnings,
640
678
  projectArtifact
641
679
  )
642
- await project.saveArtifactsToFile(projectRootDir)
680
+ await project.saveArtifactsToFile(projectRootDir, skipSaveArtifacts, changedSources)
643
681
  return project
644
682
  }
645
683
 
646
684
  private static async loadArtifacts(
647
685
  provider: NodeProvider,
648
686
  sourceInfos: SourceInfo[],
649
- projectArtifact: ProjectArtifact,
650
687
  projectRootDir: string,
651
688
  contractsRootDir: string,
652
689
  artifactsRootDir: string,
653
690
  errorOnWarnings: boolean,
654
691
  compilerOptions: node.CompilerOptions
655
692
  ): Promise<Project> {
693
+ const projectArtifact = await ProjectArtifact.from(projectRootDir)
694
+ if (projectArtifact === undefined) {
695
+ throw Error('Failed to load project artifact')
696
+ }
656
697
  try {
657
698
  const contracts = new Map<string, Compiled<Contract>>()
658
699
  const scripts = new Map<string, Compiled<Script>>()
@@ -698,7 +739,8 @@ export class Project {
698
739
  contractsRootDir,
699
740
  artifactsRootDir,
700
741
  errorOnWarnings,
701
- compilerOptions
742
+ compilerOptions,
743
+ sourceInfos
702
744
  )
703
745
  }
704
746
  }
@@ -821,19 +863,22 @@ export class Project {
821
863
  projectRootDir = '.',
822
864
  contractsRootDir = Project.DEFAULT_CONTRACTS_DIR,
823
865
  artifactsRootDir = Project.DEFAULT_ARTIFACTS_DIR,
824
- defaultFullNodeVersion: string | undefined = undefined
866
+ defaultFullNodeVersion: string | undefined = undefined,
867
+ skipSaveArtifacts = false
825
868
  ): Promise<void> {
826
869
  const provider = getCurrentNodeProvider()
827
870
  const fullNodeVersion = defaultFullNodeVersion ?? (await provider.infos.getInfosVersion()).version
828
871
  const sourceFiles = await Project.loadSourceFiles(projectRootDir, contractsRootDir)
829
872
  const { errorOnWarnings, ...nodeCompilerOptions } = { ...DEFAULT_COMPILER_OPTIONS, ...compilerOptionsPartial }
830
873
  const projectArtifact = await ProjectArtifact.from(projectRootDir)
874
+ const changedSources = projectArtifact?.getChangedSources(sourceFiles) ?? sourceFiles
831
875
  if (
832
876
  projectArtifact === undefined ||
833
- projectArtifact.needToReCompile(nodeCompilerOptions, sourceFiles, fullNodeVersion)
877
+ projectArtifact.needToReCompile(nodeCompilerOptions, fullNodeVersion) ||
878
+ changedSources.length > 0
834
879
  ) {
835
880
  if (fs.existsSync(artifactsRootDir)) {
836
- removeOldArtifacts(artifactsRootDir)
881
+ removeOldArtifacts(artifactsRootDir, sourceFiles)
837
882
  }
838
883
  console.log(`Compiling contracts in folder "${contractsRootDir}"`)
839
884
  Project.currentProject = await Project.compile(
@@ -844,21 +889,21 @@ export class Project {
844
889
  contractsRootDir,
845
890
  artifactsRootDir,
846
891
  errorOnWarnings,
847
- nodeCompilerOptions
848
- )
849
- } else {
850
- console.log(`Contracts are compiled already. Loading them from folder "${artifactsRootDir}"`)
851
- Project.currentProject = await Project.loadArtifacts(
852
- provider,
853
- sourceFiles,
854
- projectArtifact,
855
- projectRootDir,
856
- contractsRootDir,
857
- artifactsRootDir,
858
- errorOnWarnings,
859
- nodeCompilerOptions
892
+ nodeCompilerOptions,
893
+ changedSources,
894
+ skipSaveArtifacts
860
895
  )
861
896
  }
897
+ // we need to reload those contracts that did not regenerate bytecode
898
+ Project.currentProject = await Project.loadArtifacts(
899
+ provider,
900
+ sourceFiles,
901
+ projectRootDir,
902
+ contractsRootDir,
903
+ artifactsRootDir,
904
+ errorOnWarnings,
905
+ nodeCompilerOptions
906
+ )
862
907
  }
863
908
  }
864
909
 
@@ -875,18 +920,6 @@ export abstract class Artifact {
875
920
 
876
921
  abstract buildByteCodeToDeploy(initialFields: Fields, isDevnet: boolean): string
877
922
 
878
- publicFunctions(): string[] {
879
- return this.functions.filter((func) => func.isPublic).map((func) => func.name)
880
- }
881
-
882
- usingPreapprovedAssetsFunctions(): string[] {
883
- return this.functions.filter((func) => func.usePreapprovedAssets).map((func) => func.name)
884
- }
885
-
886
- usingAssetsInContractFunctions(): string[] {
887
- return this.functions.filter((func) => func.useAssetsInContract).map((func) => func.name)
888
- }
889
-
890
923
  async isDevnet(signer: SignerProvider): Promise<boolean> {
891
924
  if (!signer.nodeProvider) {
892
925
  return false
@@ -896,6 +929,16 @@ export abstract class Artifact {
896
929
  }
897
930
  }
898
931
 
932
+ function fromFunctionSig(sig: node.FunctionSig): FunctionSig {
933
+ return {
934
+ name: sig.name,
935
+ paramNames: sig.paramNames,
936
+ paramTypes: sig.paramTypes,
937
+ paramIsMutable: sig.paramIsMutable,
938
+ returnTypes: sig.returnTypes
939
+ }
940
+ }
941
+
899
942
  export class Contract extends Artifact {
900
943
  readonly bytecode: string
901
944
  readonly bytecodeDebugPatch: string
@@ -910,6 +953,7 @@ export class Contract extends Artifact {
910
953
 
911
954
  readonly bytecodeDebug: string
912
955
  readonly codeHashDebug: string
956
+ readonly decodedMethods: Method[]
913
957
 
914
958
  constructor(
915
959
  version: string,
@@ -941,6 +985,20 @@ export class Contract extends Artifact {
941
985
 
942
986
  this.bytecodeDebug = ralph.buildDebugBytecode(this.bytecode, this.bytecodeDebugPatch)
943
987
  this.codeHashDebug = codeHashDebug
988
+
989
+ this.decodedMethods = contract.contractCodec.decodeContract(Buffer.from(bytecode, 'hex')).methods
990
+ }
991
+
992
+ publicFunctions(): FunctionSig[] {
993
+ return this.functions.filter((_, index) => this.decodedMethods[`${index}`].isPublic)
994
+ }
995
+
996
+ usingPreapprovedAssetsFunctions(): FunctionSig[] {
997
+ return this.functions.filter((_, index) => this.decodedMethods[`${index}`].usePreapprovedAssets)
998
+ }
999
+
1000
+ usingAssetsInContractFunctions(): FunctionSig[] {
1001
+ return this.functions.filter((_, index) => this.decodedMethods[`${index}`].useContractAssets)
944
1002
  }
945
1003
 
946
1004
  // TODO: safely parse json
@@ -987,7 +1045,7 @@ export class Contract extends Artifact {
987
1045
  result.codeHashDebug,
988
1046
  result.fields,
989
1047
  result.events,
990
- result.functions,
1048
+ result.functions.map(fromFunctionSig),
991
1049
  result.constants,
992
1050
  result.enums,
993
1051
  structs,
@@ -1348,7 +1406,7 @@ export class Script extends Artifact {
1348
1406
  result.bytecodeTemplate,
1349
1407
  result.bytecodeDebugPatch,
1350
1408
  result.fields,
1351
- result.functions,
1409
+ result.functions.map(fromFunctionSig),
1352
1410
  structs
1353
1411
  )
1354
1412
  }
@@ -1831,14 +1889,14 @@ export function subscribeEventsFromContract<T extends Fields, M extends Contract
1831
1889
  decodeFunc: (event: node.ContractEvent) => M,
1832
1890
  fromCount?: number
1833
1891
  ): EventSubscription {
1834
- const messageCallback = (event: node.ContractEvent): Promise<void> => {
1892
+ const messageCallback = (event: node.ContractEvent) => {
1835
1893
  if (event.eventIndex !== eventIndex) {
1836
1894
  return Promise.resolve()
1837
1895
  }
1838
1896
  return options.messageCallback(decodeFunc(event))
1839
1897
  }
1840
1898
 
1841
- const errorCallback = (err: any, subscription: Subscription<node.ContractEvent>): Promise<void> => {
1899
+ const errorCallback = (err: any, subscription: Subscription<node.ContractEvent>) => {
1842
1900
  return options.errorCallback(err, subscription as unknown as Subscription<M>)
1843
1901
  }
1844
1902
  const opt: EventSubscribeOptions<node.ContractEvent> = {
@@ -1877,7 +1935,9 @@ function genCodeForType(type: string, structs: Struct[]): { bytecode: string; co
1877
1935
  const { immFields, mutFields } = ralph.calcFieldSize(type, true, structs)
1878
1936
  const loadImmFieldByIndex: Method = {
1879
1937
  isPublic: true,
1880
- assetModifier: 0,
1938
+ usePreapprovedAssets: false,
1939
+ useContractAssets: false,
1940
+ usePayToContractOnly: false,
1881
1941
  argsLength: 1,
1882
1942
  localsLength: 1,
1883
1943
  returnLength: 1,
@@ -1905,7 +1965,9 @@ function genCodeForType(type: string, structs: Struct[]): { bytecode: string; co
1905
1965
  }
1906
1966
  const destroy: Method = {
1907
1967
  isPublic: true,
1908
- assetModifier: 2,
1968
+ usePreapprovedAssets: false,
1969
+ useContractAssets: true,
1970
+ usePayToContractOnly: false,
1909
1971
  argsLength: 1,
1910
1972
  localsLength: 1,
1911
1973
  returnLength: 0,
@@ -2007,6 +2069,62 @@ export async function testMethod<
2007
2069
  } as TestContractResult<R, M>
2008
2070
  }
2009
2071
 
2072
+ export class RalphMap<K extends Val, V extends Val> {
2073
+ private readonly groupIndex: number
2074
+ constructor(
2075
+ private readonly parentContract: Contract,
2076
+ private readonly parentContractId: HexString,
2077
+ private readonly mapName: string
2078
+ ) {
2079
+ this.groupIndex = groupOfAddress(addressFromContractId(parentContractId))
2080
+ }
2081
+
2082
+ async get(key: K): Promise<V | undefined> {
2083
+ return getMapItem(this.parentContract, this.parentContractId, this.groupIndex, this.mapName, key)
2084
+ }
2085
+
2086
+ async contains(key: K): Promise<boolean> {
2087
+ return this.get(key).then((v) => v !== undefined)
2088
+ }
2089
+
2090
+ toJSON() {
2091
+ return {
2092
+ parentContractId: this.parentContractId,
2093
+ mapName: this.mapName,
2094
+ groupIndex: this.groupIndex
2095
+ }
2096
+ }
2097
+ }
2098
+
2099
+ export async function getMapItem<R extends Val>(
2100
+ parentContract: Contract,
2101
+ parentContractId: HexString,
2102
+ groupIndex: number,
2103
+ mapName: string,
2104
+ key: Val
2105
+ ): Promise<R | undefined> {
2106
+ const index = parentContract.mapsSig?.names.findIndex((name) => name === mapName)
2107
+ const mapType = index === undefined ? undefined : parentContract.mapsSig?.types[`${index}`]
2108
+ if (mapType === undefined) {
2109
+ throw new Error(`Map ${mapName} does not exist in contract ${parentContract.name}`)
2110
+ }
2111
+ const [keyType, valueType] = ralph.parseMapType(mapType)
2112
+ const mapItemContractId = calcWrapperContractId(parentContractId, index!, key, keyType, groupIndex)
2113
+ const mapItemAddress = addressFromContractId(mapItemContractId)
2114
+ try {
2115
+ const state = await getCurrentNodeProvider().contracts.getContractsAddressState(mapItemAddress)
2116
+ const fieldsSig = getContractFieldsSig(valueType)
2117
+ const fields = fromApiFields(state.immFields, state.mutFields, fieldsSig, parentContract.structs)
2118
+ return fields['value'] as R
2119
+ } catch (error) {
2120
+ if (error instanceof Error && error.message.includes('KeyNotFound')) {
2121
+ // the map item contract does not exist
2122
+ return undefined
2123
+ }
2124
+ throw error
2125
+ }
2126
+ }
2127
+
2010
2128
  interface MapInfo {
2011
2129
  name: string
2012
2130
  value: Map<Val, Val>
@@ -2221,13 +2339,13 @@ export function subscribeContractEvents(
2221
2339
  options: EventSubscribeOptions<ContractEvent<any>>,
2222
2340
  fromCount?: number
2223
2341
  ): EventSubscription {
2224
- const messageCallback = (event: node.ContractEvent): Promise<void> => {
2342
+ const messageCallback = (event: node.ContractEvent) => {
2225
2343
  return options.messageCallback({
2226
2344
  ...decodeEvent(contract, instance, event, event.eventIndex),
2227
2345
  contractAddress: instance.address
2228
2346
  })
2229
2347
  }
2230
- const errorCallback = (err: any, subscription: Subscription<node.ContractEvent>): Promise<void> => {
2348
+ const errorCallback = (err: any, subscription: Subscription<node.ContractEvent>) => {
2231
2349
  return options.errorCallback(err, subscription as unknown as Subscription<ContractEvent<any>>)
2232
2350
  }
2233
2351
  const opt: EventSubscribeOptions<node.ContractEvent> = {
@@ -2319,10 +2437,3 @@ export const getContractIdFromUnsignedTx = async (
2319
2437
 
2320
2438
  // This function only works in the simple case where a single non-subcontract is created in the tx
2321
2439
  export const getTokenIdFromUnsignedTx = getContractIdFromUnsignedTx
2322
-
2323
- export function tryGetCallResult(result: node.CallContractResult): node.CallContractSucceeded {
2324
- if (result.type === 'CallContractFailed') {
2325
- throw new Error(`Failed to call contract, error: ${(result as node.CallContractFailed).error}`)
2326
- }
2327
- return result as node.CallContractSucceeded
2328
- }
@@ -16,33 +16,24 @@ You should have received a copy of the GNU Lesser General Public License
16
16
  along with the library. If not, see <http://www.gnu.org/licenses/>.
17
17
  */
18
18
 
19
- import { web3 } from '..'
19
+ import * as web3 from '../global'
20
20
  import { node } from '../api'
21
21
  import { Subscription, SubscribeOptions } from '../utils'
22
22
 
23
23
  export interface EventSubscribeOptions<Message> extends SubscribeOptions<Message> {
24
- onEventCountChanged?: (eventCount: number) => Promise<void>
24
+ onEventCountChanged?: (eventCount: number) => Promise<void> | void
25
25
  }
26
26
 
27
27
  export class EventSubscription extends Subscription<node.ContractEvent> {
28
28
  readonly contractAddress: string
29
29
  private fromCount: number
30
- private onEventCountChanged?: (eventCount: number) => Promise<void>
30
+ private onEventCountChanged?: (eventCount: number) => Promise<void> | void
31
31
 
32
32
  constructor(options: EventSubscribeOptions<node.ContractEvent>, contractAddress: string, fromCount?: number) {
33
33
  super(options)
34
34
  this.contractAddress = contractAddress
35
35
  this.fromCount = typeof fromCount === 'undefined' ? 0 : fromCount
36
36
  this.onEventCountChanged = options.onEventCountChanged
37
-
38
- this.startPolling()
39
- }
40
-
41
- override startPolling(): void {
42
- this.eventEmitter.on('tick', async () => {
43
- await this.polling()
44
- })
45
- this.eventEmitter.emit('tick')
46
37
  }
47
38
 
48
39
  currentEventCount(): number {
@@ -54,12 +45,7 @@ export class EventSubscription extends Subscription<node.ContractEvent> {
54
45
  const events = await web3.getCurrentNodeProvider().events.getEventsContractContractaddress(this.contractAddress, {
55
46
  start: this.fromCount
56
47
  })
57
- if (this.cancelled) {
58
- return
59
- }
60
-
61
48
  if (this.fromCount === events.nextStart) {
62
- this.task = setTimeout(() => this.eventEmitter.emit('tick'), this.pollingInterval)
63
49
  return
64
50
  }
65
51
 
@@ -83,5 +69,7 @@ export function subscribeToEvents(
83
69
  contractAddress: string,
84
70
  fromCount?: number
85
71
  ): EventSubscription {
86
- return new EventSubscription(options, contractAddress, fromCount)
72
+ const subscription = new EventSubscription(options, contractAddress, fromCount)
73
+ subscription.subscribe()
74
+ return subscription
87
75
  }
package/src/index.ts CHANGED
@@ -32,3 +32,4 @@ export * as web3 from './global'
32
32
  export * as codec from './codec'
33
33
  export * as utils from './utils'
34
34
  export * from './debug'
35
+ export * from './block'
@@ -16,7 +16,7 @@ You should have received a copy of the GNU Lesser General Public License
16
16
  along with the library. If not, see <http://www.gnu.org/licenses/>.
17
17
  */
18
18
 
19
- import { utils } from '..'
19
+ import { binToHex, contractIdFromAddress } from '../utils'
20
20
  import { fromApiNumber256, node, NodeProvider, toApiNumber256Optional, toApiTokens } from '../api'
21
21
  import { addressFromPublicKey } from '../utils'
22
22
  import { toApiDestinations } from './signer'
@@ -90,7 +90,7 @@ export abstract class TransactionBuilder {
90
90
  ...rest
91
91
  }
92
92
  const response = await this.nodeProvider.contracts.postContractsUnsignedTxDeployContract(data)
93
- const contractId = utils.binToHex(utils.contractIdFromAddress(response.contractAddress))
93
+ const contractId = binToHex(contractIdFromAddress(response.contractAddress))
94
94
  return { ...response, groupIndex: response.fromGroup, contractId, gasPrice: fromApiNumber256(response.gasPrice) }
95
95
  }
96
96
 
@@ -16,7 +16,7 @@ You should have received a copy of the GNU Lesser General Public License
16
16
  along with the library. If not, see <http://www.gnu.org/licenses/>.
17
17
  */
18
18
 
19
- import { web3 } from '..'
19
+ import * as web3 from '../global'
20
20
  import { node } from '../api'
21
21
  import { Subscription, SubscribeOptions } from '../utils'
22
22
 
@@ -40,8 +40,6 @@ export class TxStatusSubscription extends Subscription<TxStatus> {
40
40
  this.fromGroup = fromGroup
41
41
  this.toGroup = toGroup
42
42
  this.confirmations = confirmations ?? 1
43
-
44
- this.startPolling()
45
43
  }
46
44
 
47
45
  override async polling(): Promise<void> {
@@ -69,5 +67,7 @@ export function subscribeToTxStatus(
69
67
  toGroup?: number,
70
68
  confirmations?: number
71
69
  ): TxStatusSubscription {
72
- return new TxStatusSubscription(options, txId, fromGroup, toGroup, confirmations)
70
+ const subscription = new TxStatusSubscription(options, txId, fromGroup, toGroup, confirmations)
71
+ subscription.subscribe()
72
+ return subscription
73
73
  }
@@ -24,6 +24,9 @@ import bs58 from './bs58'
24
24
  import djb2 from './djb2'
25
25
  import { binToHex, hexToBinUnsafe } from './utils'
26
26
  import { KeyType } from '../signer'
27
+ import { MultiSig, lockupScriptCodec } from '../codec/lockup-script-codec'
28
+ import { Buffer } from 'buffer/'
29
+ import { compactSignedIntCodec } from '../codec'
27
30
 
28
31
  const ec = new EC('secp256k1')
29
32
 
@@ -49,8 +52,18 @@ function decodeAndValidateAddress(address: string): Uint8Array {
49
52
  if (decoded.length === 0) throw new Error('Address is empty')
50
53
  const addressType = decoded[0]
51
54
  if (addressType === AddressType.P2MPKH) {
52
- // [1, n, ...hashes, m]
53
- if ((decoded.length - 3) % 32 === 0) return decoded
55
+ let multisig: MultiSig
56
+ try {
57
+ multisig = lockupScriptCodec.decode(Buffer.from(decoded)).script as MultiSig
58
+ } catch (_) {
59
+ throw new Error(`Invalid multisig address: ${address}`)
60
+ }
61
+ const n = multisig.publicKeyHashes.value.length
62
+ const m = compactSignedIntCodec.toI32(multisig.m)
63
+ if (n < m) {
64
+ throw new Error(`Invalid multisig address, n: ${n}, m: ${m}`)
65
+ }
66
+ return decoded
54
67
  } else if (addressType === AddressType.P2PKH || addressType === AddressType.P2SH || addressType === AddressType.P2C) {
55
68
  // [type, ...hash]
56
69
  if (decoded.length === 33) return decoded