@alephium/web3 0.4.0 → 0.5.0-rc.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.
@@ -33,26 +33,27 @@ import {
33
33
  Token,
34
34
  Val,
35
35
  fromApiTokens,
36
- fromApiVals
36
+ fromApiVals,
37
+ typeLength
37
38
  } from '../api'
38
39
  import {
39
40
  SignDeployContractTxParams,
41
+ SignDeployContractTxResult,
40
42
  SignExecuteScriptTxParams,
41
- SignerProvider,
42
- SignExecuteScriptTxResult,
43
- SignDeployContractTxResult
43
+ SignerProvider
44
44
  } from '../signer'
45
45
  import * as ralph from './ralph'
46
- import { bs58, binToHex, contractIdFromAddress, assertType, Eq } from '../utils'
46
+ import { bs58, binToHex, contractIdFromAddress, SubscribeOptions, Subscription, assertType, Eq } from '../utils'
47
47
  import { getCurrentNodeProvider } from '../global'
48
- import { web3 } from '..'
49
48
  import * as path from 'path'
49
+ import { EventSubscription, subscribeToEvents } from './events'
50
50
 
51
51
  export type FieldsSig = node.FieldsSig
52
52
  export type EventSig = node.EventSig
53
53
  export type FunctionSig = node.FunctionSig
54
54
  export type Fields = NamedVals
55
55
  export type Arguments = NamedVals
56
+ export type HexString = string
56
57
 
57
58
  enum SourceKind {
58
59
  Contract = 0,
@@ -354,7 +355,7 @@ export class Project {
354
355
  return script.artifact
355
356
  }
356
357
 
357
- private async saveArtifactsToFile(): Promise<void> {
358
+ private async saveArtifactsToFile(projectRootDir: string): Promise<void> {
358
359
  const artifactsRootDir = this.artifactsRootDir
359
360
  const saveToFile = async function (compiled: Compiled<Artifact>): Promise<void> {
360
361
  const artifactPath = compiled.sourceInfo.getArtifactPath(artifactsRootDir)
@@ -366,7 +367,7 @@ export class Project {
366
367
  }
367
368
  this.contracts.forEach((contract) => saveToFile(contract))
368
369
  this.scripts.forEach((script) => saveToFile(script))
369
- await this.projectArtifact.saveToFile(this.artifactsRootDir)
370
+ await this.projectArtifact.saveToFile(projectRootDir)
370
371
  }
371
372
 
372
373
  contractByCodeHash(codeHash: string): Contract {
@@ -382,6 +383,7 @@ export class Project {
382
383
  private static async compile(
383
384
  provider: NodeProvider,
384
385
  sourceInfos: SourceInfo[],
386
+ projectRootDir: string,
385
387
  contractsRootDir: string,
386
388
  artifactsRootDir: string,
387
389
  errorOnWarnings: boolean,
@@ -414,7 +416,7 @@ export class Project {
414
416
  errorOnWarnings,
415
417
  projectArtifact
416
418
  )
417
- await project.saveArtifactsToFile()
419
+ await project.saveArtifactsToFile(projectRootDir)
418
420
  return project
419
421
  }
420
422
 
@@ -422,6 +424,7 @@ export class Project {
422
424
  provider: NodeProvider,
423
425
  sourceInfos: SourceInfo[],
424
426
  projectArtifact: ProjectArtifact,
427
+ projectRootDir: string,
425
428
  contractsRootDir: string,
426
429
  artifactsRootDir: string,
427
430
  errorOnWarnings: boolean,
@@ -460,6 +463,7 @@ export class Project {
460
463
  return Project.compile(
461
464
  provider,
462
465
  sourceInfos,
466
+ projectRootDir,
463
467
  contractsRootDir,
464
468
  artifactsRootDir,
465
469
  errorOnWarnings,
@@ -590,12 +594,13 @@ export class Project {
590
594
  const provider = getCurrentNodeProvider()
591
595
  const sourceFiles = await Project.loadSourceFiles(projectRootDir, contractsRootDir)
592
596
  const { errorOnWarnings, ...nodeCompilerOptions } = { ...DEFAULT_COMPILER_OPTIONS, ...compilerOptionsPartial }
593
- const projectArtifact = await ProjectArtifact.from(artifactsRootDir)
597
+ const projectArtifact = await ProjectArtifact.from(projectRootDir)
594
598
  if (typeof projectArtifact === 'undefined' || projectArtifact.needToReCompile(nodeCompilerOptions, sourceFiles)) {
595
599
  console.log(`Compiling contracts in folder "${contractsRootDir}"`)
596
600
  Project.currentProject = await Project.compile(
597
601
  provider,
598
602
  sourceFiles,
603
+ projectRootDir,
599
604
  contractsRootDir,
600
605
  artifactsRootDir,
601
606
  errorOnWarnings,
@@ -607,6 +612,7 @@ export class Project {
607
612
  provider,
608
613
  sourceFiles,
609
614
  projectArtifact,
615
+ projectRootDir,
610
616
  contractsRootDir,
611
617
  artifactsRootDir,
612
618
  errorOnWarnings,
@@ -722,13 +728,6 @@ export class Contract extends Artifact {
722
728
  return Contract.fromJson(artifact, bytecodeDebugPatch, codeHashDebug)
723
729
  }
724
730
 
725
- async fetchState(address: string, group: number): Promise<ContractState> {
726
- const state = await web3.getCurrentNodeProvider().contracts.getContractsAddressState(address, {
727
- group: group
728
- })
729
- return this.fromApiContractState(state)
730
- }
731
-
732
731
  override toString(): string {
733
732
  const object = {
734
733
  version: this.version,
@@ -742,7 +741,7 @@ export class Contract extends Artifact {
742
741
  return JSON.stringify(object, null, 2)
743
742
  }
744
743
 
745
- toState(fields: Fields, asset: Asset, address?: string): ContractState {
744
+ toState<T extends Fields>(fields: T, asset: Asset, address?: string): ContractState<T> {
746
745
  const addressDef = typeof address !== 'undefined' ? address : Contract.randomAddress()
747
746
  return {
748
747
  address: addressDef,
@@ -763,53 +762,13 @@ export class Contract extends Artifact {
763
762
  return bs58.encode(bytes)
764
763
  }
765
764
 
766
- private _printDebugMessages(funcName: string, messages: DebugMessage[]) {
765
+ printDebugMessages(funcName: string, messages: DebugMessage[]) {
767
766
  if (messages.length != 0) {
768
767
  console.log(`Testing ${this.name}.${funcName}:`)
769
768
  messages.forEach((m) => console.log(`Debug - ${m.contractAddress} - ${m.message}`))
770
769
  }
771
770
  }
772
771
 
773
- private async _test(
774
- funcName: string,
775
- params: TestContractParams,
776
- expectPublic: boolean,
777
- accessType: string,
778
- printDebugMessages: boolean
779
- ): Promise<TestContractResult> {
780
- const apiParams: node.TestContract = this.toTestContract(funcName, params)
781
- const apiResult = await web3.getCurrentNodeProvider().contracts.postContractsTestContract(apiParams)
782
-
783
- const methodIndex =
784
- typeof params.testMethodIndex !== 'undefined' ? params.testMethodIndex : this.getMethodIndex(funcName)
785
- const isPublic = this.functions[`${methodIndex}`].isPublic
786
- if (printDebugMessages) {
787
- this._printDebugMessages(funcName, apiResult.debugMessages)
788
- }
789
- if (isPublic === expectPublic) {
790
- const result = await this.fromTestContractResult(methodIndex, apiResult)
791
- return result
792
- } else {
793
- throw new Error(`The test method ${funcName} is not ${accessType}`)
794
- }
795
- }
796
-
797
- async testPublicMethod(
798
- funcName: string,
799
- params: TestContractParams,
800
- printDebugMessages = true
801
- ): Promise<TestContractResult> {
802
- return this._test(funcName, params, true, 'public', printDebugMessages)
803
- }
804
-
805
- async testPrivateMethod(
806
- funcName: string,
807
- params: TestContractParams,
808
- printDebugMessages = true
809
- ): Promise<TestContractResult> {
810
- return this._test(funcName, params, false, 'private', printDebugMessages)
811
- }
812
-
813
772
  toApiFields(fields?: Fields): node.Val[] {
814
773
  if (typeof fields === 'undefined') {
815
774
  return []
@@ -839,12 +798,17 @@ export class Contract extends Artifact {
839
798
  return typeof states != 'undefined' ? states.map((state) => toApiContractState(state)) : undefined
840
799
  }
841
800
 
842
- toTestContract(funcName: string, params: TestContractParams): node.TestContract {
801
+ toApiTestContractParams(funcName: string, params: TestContractParams): node.TestContract {
802
+ const immFields =
803
+ params.initialFields === undefined ? [] : extractFields(params.initialFields, this.fieldsSig, false)
804
+ const mutFields =
805
+ params.initialFields === undefined ? [] : extractFields(params.initialFields, this.fieldsSig, true)
843
806
  return {
844
807
  group: params.group,
845
808
  address: params.address,
846
809
  bytecode: this.bytecodeDebug,
847
- initialFields: this.toApiFields(params.initialFields),
810
+ initialImmFields: immFields,
811
+ initialMutFields: mutFields,
848
812
  initialAsset: typeof params.initialAsset !== 'undefined' ? toApiAsset(params.initialAsset) : undefined,
849
813
  methodIndex: this.getMethodIndex(funcName),
850
814
  args: this.toApiArgs(funcName, params.testArgs),
@@ -853,14 +817,14 @@ export class Contract extends Artifact {
853
817
  }
854
818
  }
855
819
 
856
- fromApiContractState(state: node.ContractState): ContractState {
820
+ fromApiContractState(state: node.ContractState): ContractState<Fields> {
857
821
  return {
858
822
  address: state.address,
859
823
  contractId: binToHex(contractIdFromAddress(state.address)),
860
824
  bytecode: state.bytecode,
861
825
  initialStateHash: state.initialStateHash,
862
826
  codeHash: state.codeHash,
863
- fields: fromApiFields(state.fields, this.fieldsSig),
827
+ fields: fromApiFields(state.immFields, state.mutFields, this.fieldsSig),
864
828
  fieldsSig: this.fieldsSig,
865
829
  asset: fromApiAsset(state.asset)
866
830
  }
@@ -871,24 +835,26 @@ export class Contract extends Artifact {
871
835
  return contract.fromApiContractState(state)
872
836
  }
873
837
 
838
+ static ContractCreatedEventIndex = -1
874
839
  static ContractCreatedEvent: EventSig = {
875
840
  name: 'ContractCreated',
876
841
  fieldNames: ['address'],
877
842
  fieldTypes: ['Address']
878
843
  }
879
844
 
845
+ static ContractDestroyedEventIndex = -2
880
846
  static ContractDestroyedEvent: EventSig = {
881
847
  name: 'ContractDestroyed',
882
848
  fieldNames: ['address'],
883
849
  fieldTypes: ['Address']
884
850
  }
885
851
 
886
- static fromApiEvent(event: node.ContractEventByTxId, codeHash: string | undefined): ContractEventByTxId {
852
+ static fromApiEvent(event: node.ContractEventByTxId, codeHash: string | undefined, txId: string): ContractEvent {
887
853
  let eventSig: EventSig
888
854
 
889
- if (event.eventIndex == -1) {
855
+ if (event.eventIndex == Contract.ContractCreatedEventIndex) {
890
856
  eventSig = this.ContractCreatedEvent
891
- } else if (event.eventIndex == -2) {
857
+ } else if (event.eventIndex == Contract.ContractDestroyedEventIndex) {
892
858
  eventSig = this.ContractDestroyedEvent
893
859
  } else {
894
860
  const contract = Project.currentProject.contractByCodeHash(codeHash!)
@@ -896,14 +862,16 @@ export class Contract extends Artifact {
896
862
  }
897
863
 
898
864
  return {
865
+ txId: txId,
899
866
  blockHash: event.blockHash,
900
867
  contractAddress: event.contractAddress,
901
868
  name: eventSig.name,
869
+ eventIndex: event.eventIndex,
902
870
  fields: fromApiEventFields(event.fields, eventSig)
903
871
  }
904
872
  }
905
873
 
906
- fromTestContractResult(methodIndex: number, result: node.TestContractResult): TestContractResult {
874
+ fromApiTestContractResult(methodIndex: number, result: node.TestContractResult, txId: string): TestContractResult {
907
875
  const addressToCodeHash = new Map<string, string>()
908
876
  addressToCodeHash.set(result.address, result.codeHash)
909
877
  result.contracts.forEach((contract) => addressToCodeHash.set(contract.address, contract.codeHash))
@@ -914,47 +882,79 @@ export class Contract extends Artifact {
914
882
  gasUsed: result.gasUsed,
915
883
  contracts: result.contracts.map((contract) => Contract.fromApiContractState(contract)),
916
884
  txOutputs: result.txOutputs.map(fromApiOutput),
917
- events: result.events.map((event) => {
918
- const contractAddress = event.contractAddress
919
- const codeHash = addressToCodeHash.get(contractAddress)
920
- if (typeof codeHash !== 'undefined' || event.eventIndex < 0) {
921
- return Contract.fromApiEvent(event, codeHash)
922
- } else {
923
- throw Error(`Cannot find codeHash for the contract address: ${contractAddress}`)
924
- }
925
- }),
885
+ events: Contract.fromApiEvents(result.events, addressToCodeHash, txId),
926
886
  debugMessages: result.debugMessages
927
887
  }
928
888
  }
929
889
 
930
- async txParamsForDeployment(
890
+ async txParamsForDeployment<P extends Fields>(
931
891
  signer: SignerProvider,
932
- params: Omit<BuildDeployContractTx, 'signerAddress'>
892
+ params: DeployContractParams<P>
933
893
  ): Promise<SignDeployContractTxParams> {
934
- const bytecode = this.buildByteCodeToDeploy(params.initialFields ? params.initialFields : {})
894
+ const bytecode = this.buildByteCodeToDeploy(params.initialFields ?? {})
935
895
  const signerParams: SignDeployContractTxParams = {
936
896
  signerAddress: await signer.getSelectedAddress(),
937
897
  bytecode: bytecode,
938
- initialAttoAlphAmount: params.initialAttoAlphAmount,
939
- issueTokenAmount: params.issueTokenAmount,
940
- initialTokenAmounts: params.initialTokenAmounts,
941
- gasAmount: params.gasAmount,
942
- gasPrice: params.gasPrice
898
+ initialAttoAlphAmount: params?.initialAttoAlphAmount,
899
+ issueTokenAmount: params?.issueTokenAmount,
900
+ initialTokenAmounts: params?.initialTokenAmounts,
901
+ gasAmount: params?.gasAmount,
902
+ gasPrice: params?.gasPrice
943
903
  }
944
904
  return signerParams
945
905
  }
946
906
 
947
- async deploy(
948
- signer: SignerProvider,
949
- params: Omit<BuildDeployContractTx, 'signerAddress'>
950
- ): Promise<SignDeployContractTxResult> {
951
- const signerParams = await this.txParamsForDeployment(signer, params)
952
- return signer.signAndSubmitDeployContractTx(signerParams)
953
- }
954
-
955
907
  buildByteCodeToDeploy(initialFields: Fields): string {
956
908
  return ralph.buildContractByteCode(this.bytecode, initialFields, this.fieldsSig)
957
909
  }
910
+
911
+ static fromApiEvents(
912
+ events: node.ContractEventByTxId[],
913
+ addressToCodeHash: Map<string, string>,
914
+ txId: string
915
+ ): ContractEvent[] {
916
+ return events.map((event) => {
917
+ const contractAddress = event.contractAddress
918
+ const codeHash = addressToCodeHash.get(contractAddress)
919
+ if (typeof codeHash !== 'undefined' || event.eventIndex < 0) {
920
+ return Contract.fromApiEvent(event, codeHash, txId)
921
+ } else {
922
+ throw Error(`Cannot find codeHash for the contract address: ${contractAddress}`)
923
+ }
924
+ })
925
+ }
926
+
927
+ toApiCallContract<T extends Arguments>(
928
+ params: CallContractParams<T>,
929
+ groupIndex: number,
930
+ contractAddress: string,
931
+ methodIndex: number
932
+ ): node.CallContract {
933
+ const functionSig = this.functions[`${methodIndex}`]
934
+ const args = toApiVals(params.args ?? {}, functionSig.paramNames, functionSig.paramTypes)
935
+ return {
936
+ ...params,
937
+ group: groupIndex,
938
+ address: contractAddress,
939
+ methodIndex: methodIndex,
940
+ args: args
941
+ }
942
+ }
943
+
944
+ fromApiCallContractResult(result: node.CallContractResult, txId: string, methodIndex: number): CallContractResult {
945
+ const addressToCodeHash = new Map<string, string>()
946
+ result.contracts.forEach((contract) => addressToCodeHash.set(contract.address, contract.codeHash))
947
+ const functionSig = this.functions[`${methodIndex}`]
948
+ const returns = fromApiArray(result.returns, functionSig.returnTypes)
949
+ return {
950
+ returns: returns,
951
+ gasUsed: result.gasUsed,
952
+ contracts: result.contracts.map((state) => Contract.fromApiContractState(state)),
953
+ txInputs: result.txInputs,
954
+ txOutputs: result.txOutputs.map((output) => fromApiOutput(output)),
955
+ events: Contract.fromApiEvents(result.events, addressToCodeHash, txId)
956
+ }
957
+ }
958
958
  }
959
959
 
960
960
  export class Script extends Artifact {
@@ -1025,13 +1025,13 @@ export class Script extends Artifact {
1025
1025
  return JSON.stringify(object, null, 2)
1026
1026
  }
1027
1027
 
1028
- async txParamsForExecution(
1028
+ async txParamsForExecution<P extends Fields>(
1029
1029
  signer: SignerProvider,
1030
- params: Omit<BuildExecuteScriptTx, 'signerAddress'>
1030
+ params: ExecuteScriptParams<P>
1031
1031
  ): Promise<SignExecuteScriptTxParams> {
1032
1032
  const signerParams: SignExecuteScriptTxParams = {
1033
1033
  signerAddress: await signer.getSelectedAddress(),
1034
- bytecode: this.buildByteCodeToDeploy(params.initialFields ? params.initialFields : {}),
1034
+ bytecode: this.buildByteCodeToDeploy(params.initialFields ?? {}),
1035
1035
  attoAlphAmount: params.attoAlphAmount,
1036
1036
  tokens: params.tokens,
1037
1037
  gasAmount: params.gasAmount,
@@ -1040,20 +1040,27 @@ export class Script extends Artifact {
1040
1040
  return signerParams
1041
1041
  }
1042
1042
 
1043
- async execute(
1044
- signer: SignerProvider,
1045
- params: Omit<BuildExecuteScriptTx, 'signerAddress'>
1046
- ): Promise<SignExecuteScriptTxResult> {
1047
- const signerParams = await this.txParamsForExecution(signer, params)
1048
- return await signer.signAndSubmitExecuteScriptTx(signerParams)
1049
- }
1050
-
1051
1043
  buildByteCodeToDeploy(initialFields: Fields): string {
1052
1044
  return ralph.buildScriptByteCode(this.bytecodeTemplate, initialFields, this.fieldsSig)
1053
1045
  }
1054
1046
  }
1055
1047
 
1056
- function fromApiFields(vals: node.Val[], fieldsSig: node.FieldsSig): Fields {
1048
+ function fromApiFields(immFields: node.Val[], mutFields: node.Val[], fieldsSig: node.FieldsSig): Fields {
1049
+ const vals: node.Val[] = []
1050
+ let immIndex = 0
1051
+ let mutIndex = 0
1052
+ const isMutable = fieldsSig.types.flatMap((tpe, index) =>
1053
+ Array(typeLength(tpe)).fill(fieldsSig.isMutable[`${index}`])
1054
+ )
1055
+ isMutable.forEach((mutable) => {
1056
+ if (mutable) {
1057
+ vals.push(mutFields[`${mutIndex}`])
1058
+ mutIndex += 1
1059
+ } else {
1060
+ vals.push(immFields[`${immIndex}`])
1061
+ immIndex += 1
1062
+ }
1063
+ })
1057
1064
  return fromApiVals(vals, fieldsSig.names, fieldsSig.types)
1058
1065
  }
1059
1066
 
@@ -1085,13 +1092,13 @@ export interface InputAsset {
1085
1092
  asset: Asset
1086
1093
  }
1087
1094
 
1088
- export interface ContractState {
1095
+ export interface ContractState<T extends Fields = Fields> {
1089
1096
  address: string
1090
1097
  contractId: string
1091
1098
  bytecode: string
1092
1099
  initialStateHash?: string
1093
1100
  codeHash: string
1094
- fields: Fields
1101
+ fields: T
1095
1102
  fieldsSig: FieldsSig
1096
1103
  asset: Asset
1097
1104
  }
@@ -1104,13 +1111,24 @@ function getVal(vals: NamedVals, name: string): Val {
1104
1111
  }
1105
1112
  }
1106
1113
 
1114
+ function extractFields(fields: NamedVals, fieldsSig: FieldsSig, mutable: boolean) {
1115
+ const fieldIndexes = fieldsSig.names
1116
+ .map((_, index) => index)
1117
+ .filter((index) => fieldsSig.isMutable[`${index}`] === mutable)
1118
+ const fieldNames = fieldIndexes.map((index) => fieldsSig.names[`${index}`])
1119
+ const fieldTypes = fieldIndexes.map((index) => fieldsSig.types[`${index}`])
1120
+ return toApiVals(fields, fieldNames, fieldTypes)
1121
+ }
1122
+
1107
1123
  function toApiContractState(state: ContractState): node.ContractState {
1124
+ const stateFields = state.fields ?? {}
1108
1125
  return {
1109
1126
  address: state.address,
1110
1127
  bytecode: state.bytecode,
1111
1128
  codeHash: state.codeHash,
1112
1129
  initialStateHash: state.initialStateHash,
1113
- fields: toApiFields(state.fields, state.fieldsSig),
1130
+ immFields: extractFields(stateFields, state.fieldsSig, false),
1131
+ mutFields: extractFields(stateFields, state.fieldsSig, true),
1114
1132
  asset: toApiAsset(state.asset)
1115
1133
  }
1116
1134
  }
@@ -1123,7 +1141,7 @@ function toApiArgs(args: Arguments, funcSig: FunctionSig): node.Val[] {
1123
1141
  return toApiVals(args, funcSig.paramNames, funcSig.paramTypes)
1124
1142
  }
1125
1143
 
1126
- function toApiVals(fields: Fields, names: string[], types: string[]): node.Val[] {
1144
+ export function toApiVals(fields: Fields, names: string[], types: string[]): node.Val[] {
1127
1145
  return names.map((name, index) => {
1128
1146
  const val = getVal(fields, name)
1129
1147
  const tpe = types[`${index}`]
@@ -1139,29 +1157,25 @@ function toApiInputAssets(inputAssets?: InputAsset[]): node.TestInputAsset[] | u
1139
1157
  return typeof inputAssets !== 'undefined' ? inputAssets.map(toApiInputAsset) : undefined
1140
1158
  }
1141
1159
 
1142
- export interface TestContractParams {
1160
+ export interface TestContractParams<F extends Fields = Fields, A extends Arguments = Arguments> {
1143
1161
  group?: number // default 0
1144
1162
  address?: string
1145
- initialFields?: Fields // default no fields
1163
+ blockHash?: string
1164
+ txId?: string
1165
+ initialFields: F
1146
1166
  initialAsset?: Asset // default 1 ALPH
1147
- testMethodIndex?: number // default 0
1148
- testArgs?: Arguments // default no arguments
1167
+ testArgs: A
1149
1168
  existingContracts?: ContractState[] // default no existing contracts
1150
1169
  inputAssets?: InputAsset[] // default no input asserts
1151
1170
  }
1152
1171
 
1153
- export interface ContractEvent {
1154
- blockHash: string
1172
+ export interface ContractEvent<T extends Fields = Fields> {
1155
1173
  txId: string
1156
- name: string
1157
- fields: Fields
1158
- }
1159
-
1160
- export interface ContractEventByTxId {
1161
1174
  blockHash: string
1162
1175
  contractAddress: string
1176
+ eventIndex: number
1163
1177
  name: string
1164
- fields: Fields
1178
+ fields: T
1165
1179
  }
1166
1180
 
1167
1181
  export type DebugMessage = node.DebugMessage
@@ -1173,7 +1187,7 @@ export interface TestContractResult {
1173
1187
  gasUsed: number
1174
1188
  contracts: ContractState[]
1175
1189
  txOutputs: Output[]
1176
- events: ContractEventByTxId[]
1190
+ events: ContractEvent[]
1177
1191
  debugMessages: DebugMessage[]
1178
1192
  }
1179
1193
  export declare type Output = AssetOutput | ContractOutput
@@ -1214,41 +1228,135 @@ function fromApiOutput(output: node.Output): Output {
1214
1228
  }
1215
1229
  }
1216
1230
 
1217
- export interface DeployContractTransaction {
1218
- fromGroup: number
1219
- toGroup: number
1220
- unsignedTx: string
1221
- txId: string
1222
- contractAddress: string
1223
- contractId: string
1231
+ export function randomTxId(): string {
1232
+ const bytes = new Uint8Array(32)
1233
+ crypto.getRandomValues(bytes)
1234
+ return binToHex(bytes)
1224
1235
  }
1225
1236
 
1226
- type BuildTxParams<T> = Omit<T, 'bytecode'> & { initialFields?: Val[] }
1227
-
1228
- export interface BuildDeployContractTx {
1229
- signerAddress: string
1230
- initialFields?: Fields
1237
+ export interface DeployContractParams<P extends Fields = Fields> {
1238
+ initialFields: P
1231
1239
  initialAttoAlphAmount?: Number256
1232
1240
  initialTokenAmounts?: Token[]
1233
1241
  issueTokenAmount?: Number256
1234
1242
  gasAmount?: number
1235
1243
  gasPrice?: Number256
1236
1244
  }
1237
- assertType<Eq<keyof BuildDeployContractTx, keyof BuildTxParams<SignDeployContractTxParams>>>()
1245
+ assertType<
1246
+ Eq<
1247
+ Omit<DeployContractParams<undefined>, 'initialFields'>,
1248
+ Omit<SignDeployContractTxParams, 'signerAddress' | 'bytecode'>
1249
+ >
1250
+ >
1251
+ export type DeployContractResult<T> = SignDeployContractTxResult & { instance: T }
1252
+
1253
+ export abstract class ContractFactory<T, P extends Fields = Fields> {
1254
+ readonly contract: Contract
1255
+ constructor(contract: Contract) {
1256
+ this.contract = contract
1257
+ }
1258
+
1259
+ async deploy(signer: SignerProvider, deployParams: DeployContractParams<P>): Promise<DeployContractResult<T>> {
1260
+ const signerParams = await this.contract.txParamsForDeployment(signer, deployParams)
1261
+ const result = await signer.signAndSubmitDeployContractTx(signerParams)
1262
+ return {
1263
+ ...result,
1264
+ instance: this.at(result.contractAddress)
1265
+ }
1266
+ }
1238
1267
 
1239
- export interface BuildExecuteScriptTx {
1240
- signerAddress: string
1241
- initialFields?: Fields
1268
+ abstract at(address: string): T
1269
+ }
1270
+
1271
+ export interface ExecuteScriptParams<P extends Fields = Fields> {
1272
+ initialFields: P
1242
1273
  attoAlphAmount?: Number256
1243
1274
  tokens?: Token[]
1244
1275
  gasAmount?: number
1245
1276
  gasPrice?: Number256
1246
1277
  }
1247
- assertType<Eq<keyof BuildExecuteScriptTx, keyof BuildTxParams<SignExecuteScriptTxParams>>>()
1248
1278
 
1249
- export interface BuildScriptTxResult {
1250
- fromGroup: number
1251
- toGroup: number
1279
+ export interface ExecuteScriptResult {
1280
+ groupIndex: number
1252
1281
  unsignedTx: string
1253
1282
  txId: string
1283
+ signature: string
1284
+ gasAmount: number
1285
+ gasPrice: Number256
1286
+ }
1287
+
1288
+ export interface CallContractParams<T extends Arguments = Arguments> {
1289
+ args: T
1290
+ worldStateBlockHash?: string
1291
+ txId?: string
1292
+ existingContracts?: string[]
1293
+ inputAssets?: node.TestInputAsset[]
1294
+ }
1295
+
1296
+ export interface CallContractResult {
1297
+ returns: Val[]
1298
+ gasUsed: number
1299
+ contracts: ContractState[]
1300
+ txInputs: string[]
1301
+ txOutputs: Output[]
1302
+ events: ContractEvent[]
1303
+ }
1304
+
1305
+ export type ContractCreatedEvent = ContractEvent<{ address: HexString }>
1306
+ export type ContractDestroyedEvent = ContractEvent<{ address: HexString }>
1307
+
1308
+ function decodeFields(event: node.ContractEvent, eventSig: EventSig, eventIndex: number): Fields {
1309
+ if (event.eventIndex !== eventIndex) {
1310
+ throw new Error(`Invalid event index: ${event.eventIndex}, expected: ${eventIndex}`)
1311
+ }
1312
+ return fromApiVals(event.fields, eventSig.fieldNames, eventSig.fieldTypes)
1313
+ }
1314
+
1315
+ export function decodeContractCreatedEvent(event: node.ContractEvent): Omit<ContractCreatedEvent, 'contractAddress'> {
1316
+ const fields = decodeFields(event, Contract.ContractCreatedEvent, Contract.ContractCreatedEventIndex)
1317
+ return {
1318
+ blockHash: event.blockHash,
1319
+ txId: event.txId,
1320
+ eventIndex: event.eventIndex,
1321
+ name: Contract.ContractCreatedEvent.name,
1322
+ fields: { address: fields['address'] as HexString }
1323
+ }
1324
+ }
1325
+
1326
+ export function decodeContractDestroyedEvent(
1327
+ event: node.ContractEvent
1328
+ ): Omit<ContractDestroyedEvent, 'contractAddress'> {
1329
+ const fields = decodeFields(event, Contract.ContractDestroyedEvent, Contract.ContractDestroyedEventIndex)
1330
+ return {
1331
+ blockHash: event.blockHash,
1332
+ txId: event.txId,
1333
+ eventIndex: event.eventIndex,
1334
+ name: Contract.ContractDestroyedEvent.name,
1335
+ fields: { address: fields['address'] as HexString }
1336
+ }
1337
+ }
1338
+
1339
+ export function subscribeEventsFromContract<T extends Fields>(
1340
+ options: SubscribeOptions<ContractEvent<T>>,
1341
+ address: string,
1342
+ eventIndex: number,
1343
+ decodeFunc: (event: node.ContractEvent) => ContractEvent<T>,
1344
+ fromCount?: number
1345
+ ): EventSubscription {
1346
+ const messageCallback = (event: node.ContractEvent): Promise<void> => {
1347
+ if (event.eventIndex !== eventIndex) {
1348
+ return Promise.resolve()
1349
+ }
1350
+ return options.messageCallback(decodeFunc(event))
1351
+ }
1352
+
1353
+ const errorCallback = (err: any, subscription: Subscription<node.ContractEvent>): Promise<void> => {
1354
+ return options.errorCallback(err, subscription as unknown as Subscription<ContractEvent<T>>)
1355
+ }
1356
+ const opt: SubscribeOptions<node.ContractEvent> = {
1357
+ pollingInterval: options.pollingInterval,
1358
+ messageCallback: messageCallback,
1359
+ errorCallback: errorCallback
1360
+ }
1361
+ return subscribeToEvents(opt, address, fromCount)
1254
1362
  }