@alephium/web3 0.40.0 → 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 (58) 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/node-provider.d.ts +2 -0
  4. package/dist/src/api/node-provider.js +12 -6
  5. package/dist/src/api/utils.d.ts +1 -1
  6. package/dist/src/block/block.d.ts +28 -0
  7. package/dist/src/block/block.js +131 -0
  8. package/dist/src/block/index.d.ts +1 -0
  9. package/dist/src/block/index.js +22 -0
  10. package/dist/src/codec/contract-output-codec.js +4 -4
  11. package/dist/src/codec/lockup-script-codec.js +2 -2
  12. package/dist/src/codec/method-codec.d.ts +3 -1
  13. package/dist/src/codec/method-codec.js +27 -2
  14. package/dist/src/codec/script-codec.d.ts +11 -6
  15. package/dist/src/codec/script-codec.js +13 -2
  16. package/dist/src/codec/transaction-codec.js +2 -2
  17. package/dist/src/codec/unlock-script-codec.d.ts +2 -2
  18. package/dist/src/codec/unsigned-tx-codec.d.ts +2 -2
  19. package/dist/src/contract/contract.d.ts +19 -10
  20. package/dist/src/contract/contract.js +103 -56
  21. package/dist/src/contract/events.d.ts +1 -2
  22. package/dist/src/contract/events.js +28 -14
  23. package/dist/src/index.d.ts +1 -0
  24. package/dist/src/index.js +1 -0
  25. package/dist/src/signer/tx-builder.js +4 -4
  26. package/dist/src/transaction/status.js +28 -4
  27. package/dist/src/utils/address.js +29 -16
  28. package/dist/src/utils/exchange.js +25 -15
  29. package/dist/src/utils/number.d.ts +1 -1
  30. package/dist/src/utils/sign.js +6 -6
  31. package/dist/src/utils/subscription.d.ts +4 -4
  32. package/dist/src/utils/subscription.js +1 -1
  33. package/package.json +3 -3
  34. package/src/api/node-provider.ts +8 -1
  35. package/src/api/utils.ts +1 -1
  36. package/src/block/block.ts +139 -0
  37. package/src/block/index.ts +19 -0
  38. package/src/codec/contract-output-codec.ts +1 -1
  39. package/src/codec/lockup-script-codec.ts +3 -3
  40. package/src/codec/method-codec.ts +41 -3
  41. package/src/codec/script-codec.ts +23 -5
  42. package/src/codec/transaction-codec.ts +1 -1
  43. package/src/codec/unlock-script-codec.ts +2 -2
  44. package/src/codec/unsigned-tx-codec.ts +2 -2
  45. package/src/contract/contract.ts +139 -78
  46. package/src/contract/events.ts +6 -18
  47. package/src/index.ts +1 -0
  48. package/src/signer/tx-builder.ts +2 -2
  49. package/src/transaction/status.ts +4 -4
  50. package/src/utils/address.ts +15 -2
  51. package/src/utils/exchange.ts +32 -10
  52. package/src/utils/number.ts +1 -1
  53. package/src/utils/sign.ts +1 -1
  54. package/src/utils/subscription.ts +4 -4
  55. package/std/fungible_token_interface.ral +1 -0
  56. package/std/nft_collection_interface.ral +1 -0
  57. package/std/nft_collection_with_royalty_interface.ral +1 -0
  58. package/std/nft_interface.ral +1 -0
@@ -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,
@@ -2008,24 +2070,36 @@ export async function testMethod<
2008
2070
  }
2009
2071
 
2010
2072
  export class RalphMap<K extends Val, V extends Val> {
2073
+ private readonly groupIndex: number
2011
2074
  constructor(
2012
2075
  private readonly parentContract: Contract,
2013
- private readonly parentInstance: ContractInstance,
2076
+ private readonly parentContractId: HexString,
2014
2077
  private readonly mapName: string
2015
- ) {}
2078
+ ) {
2079
+ this.groupIndex = groupOfAddress(addressFromContractId(parentContractId))
2080
+ }
2016
2081
 
2017
2082
  async get(key: K): Promise<V | undefined> {
2018
- return getMapItem(this.parentContract, this.parentInstance, this.mapName, key)
2083
+ return getMapItem(this.parentContract, this.parentContractId, this.groupIndex, this.mapName, key)
2019
2084
  }
2020
2085
 
2021
2086
  async contains(key: K): Promise<boolean> {
2022
2087
  return this.get(key).then((v) => v !== undefined)
2023
2088
  }
2089
+
2090
+ toJSON() {
2091
+ return {
2092
+ parentContractId: this.parentContractId,
2093
+ mapName: this.mapName,
2094
+ groupIndex: this.groupIndex
2095
+ }
2096
+ }
2024
2097
  }
2025
2098
 
2026
2099
  export async function getMapItem<R extends Val>(
2027
2100
  parentContract: Contract,
2028
- parentInstance: ContractInstance,
2101
+ parentContractId: HexString,
2102
+ groupIndex: number,
2029
2103
  mapName: string,
2030
2104
  key: Val
2031
2105
  ): Promise<R | undefined> {
@@ -2035,13 +2109,7 @@ export async function getMapItem<R extends Val>(
2035
2109
  throw new Error(`Map ${mapName} does not exist in contract ${parentContract.name}`)
2036
2110
  }
2037
2111
  const [keyType, valueType] = ralph.parseMapType(mapType)
2038
- const mapItemContractId = calcWrapperContractId(
2039
- parentInstance.contractId,
2040
- index!,
2041
- key,
2042
- keyType,
2043
- parentInstance.groupIndex
2044
- )
2112
+ const mapItemContractId = calcWrapperContractId(parentContractId, index!, key, keyType, groupIndex)
2045
2113
  const mapItemAddress = addressFromContractId(mapItemContractId)
2046
2114
  try {
2047
2115
  const state = await getCurrentNodeProvider().contracts.getContractsAddressState(mapItemAddress)
@@ -2271,13 +2339,13 @@ export function subscribeContractEvents(
2271
2339
  options: EventSubscribeOptions<ContractEvent<any>>,
2272
2340
  fromCount?: number
2273
2341
  ): EventSubscription {
2274
- const messageCallback = (event: node.ContractEvent): Promise<void> => {
2342
+ const messageCallback = (event: node.ContractEvent) => {
2275
2343
  return options.messageCallback({
2276
2344
  ...decodeEvent(contract, instance, event, event.eventIndex),
2277
2345
  contractAddress: instance.address
2278
2346
  })
2279
2347
  }
2280
- const errorCallback = (err: any, subscription: Subscription<node.ContractEvent>): Promise<void> => {
2348
+ const errorCallback = (err: any, subscription: Subscription<node.ContractEvent>) => {
2281
2349
  return options.errorCallback(err, subscription as unknown as Subscription<ContractEvent<any>>)
2282
2350
  }
2283
2351
  const opt: EventSubscribeOptions<node.ContractEvent> = {
@@ -2369,10 +2437,3 @@ export const getContractIdFromUnsignedTx = async (
2369
2437
 
2370
2438
  // This function only works in the simple case where a single non-subcontract is created in the tx
2371
2439
  export const getTokenIdFromUnsignedTx = getContractIdFromUnsignedTx
2372
-
2373
- export function tryGetCallResult(result: node.CallContractResult): node.CallContractSucceeded {
2374
- if (result.type === 'CallContractFailed') {
2375
- throw new Error(`Failed to call contract, error: ${(result as node.CallContractFailed).error}`)
2376
- }
2377
- return result as node.CallContractSucceeded
2378
- }
@@ -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
@@ -16,9 +16,20 @@ 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 { AddressType, addressFromPublicKey, addressFromScript, binToHex, bs58, hexToBinUnsafe } from '..'
19
+ import {
20
+ AddressType,
21
+ addressFromPublicKey,
22
+ addressFromScript,
23
+ binToHex,
24
+ bs58,
25
+ hexToBinUnsafe,
26
+ isHexString
27
+ } from '../utils'
20
28
  import { Transaction } from '../api/api-alephium'
21
29
  import { Address } from '../signer'
30
+ import { P2SH, unlockScriptCodec } from '../codec/unlock-script-codec'
31
+ import { Buffer } from 'buffer/'
32
+ import { scriptCodec } from '../codec/script-codec'
22
33
 
23
34
  export function validateExchangeAddress(address: string) {
24
35
  let decoded: Uint8Array
@@ -80,24 +91,35 @@ enum UnlockScriptType {
80
91
  }
81
92
 
82
93
  export function getAddressFromUnlockScript(unlockScript: string): Address {
94
+ if (!isHexString(unlockScript)) {
95
+ throw new Error(`Invalid unlock script ${unlockScript}, expected a hex string`)
96
+ }
83
97
  const decoded = hexToBinUnsafe(unlockScript)
84
98
  if (decoded.length === 0) throw new Error('UnlockScript is empty')
85
99
  const unlockScriptType = decoded[0]
86
100
  const unlockScriptBody = decoded.slice(1)
87
101
 
88
102
  if (unlockScriptType === UnlockScriptType.P2PKH) {
103
+ if (unlockScriptBody.length !== 33) {
104
+ throw new Error(`Invalid p2pkh unlock script: ${unlockScript}`)
105
+ }
89
106
  return addressFromPublicKey(binToHex(unlockScriptBody))
90
- } else if (unlockScriptType === UnlockScriptType.P2MPKH) {
107
+ }
108
+
109
+ if (unlockScriptType === UnlockScriptType.P2MPKH) {
91
110
  throw new Error('Naive multi-sig address is not supported for exchanges as it will be replaced by P2SH')
92
- } else if (unlockScriptType === UnlockScriptType.P2SH) {
93
- // FIXEME: for now we assume that the params is empty, so we need to
94
- // remove the last byte from the `unlockScriptBody`, we can decode
95
- // the unlock script once the codec PR is merged
96
- const script = unlockScriptBody.slice(0, -1)
97
- return addressFromScript(script)
98
- } else {
99
- throw new Error('Invalid unlock script type')
100
111
  }
112
+
113
+ if (unlockScriptType === UnlockScriptType.P2SH) {
114
+ let p2sh: P2SH
115
+ try {
116
+ p2sh = unlockScriptCodec.decode(Buffer.from(decoded)).script as P2SH
117
+ } catch (_) {
118
+ throw new Error(`Invalid p2sh unlock script: ${unlockScript}`)
119
+ }
120
+ return addressFromScript(scriptCodec.encode(p2sh.script))
121
+ }
122
+ throw new Error('Invalid unlock script type')
101
123
  }
102
124
 
103
125
  function checkALPHOutput(tx: Transaction): boolean {
@@ -21,7 +21,7 @@ along with the library. If not, see <http://www.gnu.org/licenses/>.
21
21
  // 2. https://github.com/ethers-io/ethers.js/blob/724881f34d428406488a1c9f9dbebe54b6edecda/src.ts/utils/fixednumber.ts
22
22
 
23
23
  import BigNumber from 'bignumber.js'
24
- import { Number256 } from '..'
24
+ import { Number256 } from '../api/types'
25
25
 
26
26
  export const isNumeric = (numToCheck: any): boolean => !isNaN(parseFloat(numToCheck)) && isFinite(numToCheck)
27
27
 
package/src/utils/sign.ts CHANGED
@@ -17,7 +17,7 @@ along with the library. If not, see <http://www.gnu.org/licenses/>.
17
17
  */
18
18
 
19
19
  import { ec as EC } from 'elliptic'
20
- import { binToHex, encodeSignature, hexToBinUnsafe, signatureDecode } from '..'
20
+ import { binToHex, encodeSignature, hexToBinUnsafe, signatureDecode } from '../utils'
21
21
  import { KeyType } from '../signer'
22
22
  import * as necc from '@noble/secp256k1'
23
23
  import { createHash, createHmac } from 'crypto'