@alephium/web3 1.7.3 → 1.8.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.
@@ -2046,12 +2046,14 @@ function toFieldsSig(contractName: string, functionSig: FunctionSig): FieldsSig
2046
2046
  }
2047
2047
  }
2048
2048
 
2049
+ type Calls = Record<string, Optional<CallContractParams<any>, 'args'>>
2049
2050
  export async function multicallMethods<I extends ContractInstance, F extends Fields>(
2050
2051
  contract: ContractFactory<I, F>,
2051
2052
  instance: ContractInstance,
2052
- callss: Record<string, Optional<CallContractParams<any>, 'args'>>[],
2053
+ _callss: Calls | Calls[],
2053
2054
  getContractByCodeHash: (codeHash: string) => Contract
2054
2055
  ): Promise<Record<string, CallContractResult<any>>[] | Record<string, CallContractResult<any>>> {
2056
+ const callss = Array.isArray(_callss) ? _callss : [_callss]
2055
2057
  const callEntries = callss.map((calls) => Object.entries(calls))
2056
2058
  const callsParams = callEntries.map((entries) => {
2057
2059
  return entries.map((entry) => {
@@ -2068,7 +2070,7 @@ export async function multicallMethods<I extends ContractInstance, F extends Fie
2068
2070
  })
2069
2071
  const result = await getCurrentNodeProvider().contracts.postContractsMulticallContract({ calls: callsParams.flat() })
2070
2072
  let callResultIndex = 0
2071
- return callsParams.map((calls, index0) => {
2073
+ const results = callsParams.map((calls, index0) => {
2072
2074
  const callsResult: Record<string, CallContractResult<any>> = {}
2073
2075
  const entries = callEntries[`${index0}`]
2074
2076
  calls.forEach((call, index1) => {
@@ -2085,6 +2087,7 @@ export async function multicallMethods<I extends ContractInstance, F extends Fie
2085
2087
  })
2086
2088
  return callsResult
2087
2089
  })
2090
+ return Array.isArray(_callss) ? results : results[0]
2088
2091
  }
2089
2092
 
2090
2093
  export async function getContractEventsCurrentCount(contractAddress: Address): Promise<number> {
@@ -28,7 +28,6 @@ import {
28
28
  Destination,
29
29
  SignDeployContractTxParams,
30
30
  SignDeployContractTxResult,
31
- SignerAddress,
32
31
  SignExecuteScriptTxParams,
33
32
  SignExecuteScriptTxResult,
34
33
  SignMessageParams,
@@ -40,7 +39,9 @@ import {
40
39
  SubmissionResult,
41
40
  SubmitTransactionParams,
42
41
  KeyType,
43
- MessageHasher
42
+ MessageHasher,
43
+ SignChainedTxParams,
44
+ SignChainedTxResult
44
45
  } from './types'
45
46
  import { TransactionBuilder } from './tx-builder'
46
47
  import { addressFromPublicKey, groupOfAddress } from '../address'
@@ -68,6 +69,7 @@ export abstract class SignerProvider {
68
69
  abstract signAndSubmitDeployContractTx(params: SignDeployContractTxParams): Promise<SignDeployContractTxResult>
69
70
  abstract signAndSubmitExecuteScriptTx(params: SignExecuteScriptTxParams): Promise<SignExecuteScriptTxResult>
70
71
  abstract signAndSubmitUnsignedTx(params: SignUnsignedTxParams): Promise<SignUnsignedTxResult>
72
+ abstract signAndSubmitChainedTx(params: SignChainedTxParams[]): Promise<SignChainedTxResult[]>
71
73
 
72
74
  abstract signUnsignedTx(params: SignUnsignedTxParams): Promise<SignUnsignedTxResult>
73
75
  // The message will be prefixed with 'Alephium Signed Message: ' before signing
@@ -117,17 +119,16 @@ export abstract class SignerProviderSimple extends SignerProvider {
117
119
  await this.submitTransaction(signResult)
118
120
  return signResult
119
121
  }
122
+ override async signAndSubmitChainedTx(params: SignChainedTxParams[]): Promise<SignChainedTxResult[]> {
123
+ const signResults = await this.signChainedTx(params)
124
+ for (const r of signResults) {
125
+ await this.submitTransaction(r)
126
+ }
127
+ return signResults
128
+ }
120
129
 
121
130
  protected abstract getPublicKey(address: string): Promise<string>
122
131
 
123
- private async usePublicKey<T extends SignerAddress>(
124
- params: T
125
- ): Promise<Omit<T, 'signerAddress'> & { fromPublicKey: string }> {
126
- const { signerAddress, ...restParams } = params
127
- const publicKey = await this.getPublicKey(signerAddress)
128
- return { fromPublicKey: publicKey, ...restParams }
129
- }
130
-
131
132
  async signTransferTx(params: SignTransferTxParams): Promise<SignTransferTxResult> {
132
133
  const response = await this.buildTransferTx(params)
133
134
  const signature = await this.signRaw(params.signerAddress, response.txId)
@@ -169,6 +170,19 @@ export abstract class SignerProviderSimple extends SignerProvider {
169
170
  )
170
171
  }
171
172
 
173
+ async signChainedTx(params: SignChainedTxParams[]): Promise<SignChainedTxResult[]> {
174
+ const response = await this.buildChainedTx(params)
175
+ const signatures = await Promise.all(response.map((r, i) => this.signRaw(params[`${i}`].signerAddress, r.txId)))
176
+ return response.map((r, i) => ({ ...r, signature: signatures[`${i}`] } as SignChainedTxResult))
177
+ }
178
+
179
+ async buildChainedTx(params: SignChainedTxParams[]): Promise<Omit<SignChainedTxResult, 'signature'>[]> {
180
+ return TransactionBuilder.from(this.nodeProvider).buildChainedTx(
181
+ params,
182
+ await Promise.all(params.map((p) => this.getPublicKey(p.signerAddress)))
183
+ )
184
+ }
185
+
172
186
  // in general, wallet should show the decoded information to user for confirmation
173
187
  // please overwrite this function for real wallet
174
188
  async signUnsignedTx(params: SignUnsignedTxParams): Promise<SignUnsignedTxResult> {
@@ -21,20 +21,26 @@ import { fromApiNumber256, node, NodeProvider, toApiNumber256Optional, toApiToke
21
21
  import { addressFromPublicKey, contractIdFromAddress } from '../address'
22
22
  import { toApiDestinations } from './signer'
23
23
  import {
24
+ SignChainedTxParams,
25
+ SignChainedTxResult,
24
26
  KeyType,
27
+ SignDeployContractChainedTxResult,
25
28
  SignDeployContractTxParams,
26
29
  SignDeployContractTxResult,
27
30
  SignerAddress,
31
+ SignExecuteScriptChainedTxResult,
28
32
  SignExecuteScriptTxParams,
29
33
  SignExecuteScriptTxResult,
34
+ SignTransferChainedTxResult,
30
35
  SignTransferTxParams,
31
36
  SignTransferTxResult,
32
37
  SignUnsignedTxParams,
33
38
  SignUnsignedTxResult
34
39
  } from './types'
35
- import { unsignedTxCodec, UnsignedTxCodec } from '../codec'
40
+ import { unsignedTxCodec } from '../codec'
36
41
  import { groupIndexOfTransaction } from '../transaction'
37
42
  import { blakeHash } from '../codec/hash'
43
+ import { BuildDeployContractTxResult, BuildChainedTx, BuildChainedTxResult } from '../api/api-alephium'
38
44
 
39
45
  export abstract class TransactionBuilder {
40
46
  abstract get nodeProvider(): NodeProvider
@@ -62,28 +68,129 @@ export abstract class TransactionBuilder {
62
68
  params: SignTransferTxParams,
63
69
  publicKey: string
64
70
  ): Promise<Omit<SignTransferTxResult, 'signature'>> {
71
+ const data = this.buildTransferTxParams(params, publicKey)
72
+ const response = await this.nodeProvider.transactions.postTransactionsBuild(data)
73
+ return this.convertTransferTxResult(response)
74
+ }
75
+
76
+ async buildDeployContractTx(
77
+ params: SignDeployContractTxParams,
78
+ publicKey: string
79
+ ): Promise<Omit<SignDeployContractTxResult, 'signature'>> {
80
+ const data = this.buildDeployContractTxParams(params, publicKey)
81
+ const response = await this.nodeProvider.contracts.postContractsUnsignedTxDeployContract(data)
82
+ return this.convertDeployContractTxResult(response)
83
+ }
84
+
85
+ async buildExecuteScriptTx(
86
+ params: SignExecuteScriptTxParams,
87
+ publicKey: string
88
+ ): Promise<Omit<SignExecuteScriptTxResult, 'signature'>> {
89
+ const data = this.buildExecuteScriptTxParams(params, publicKey)
90
+ const response = await this.nodeProvider.contracts.postContractsUnsignedTxExecuteScript(data)
91
+ return this.convertExecuteScriptTxResult(response)
92
+ }
93
+
94
+ async buildChainedTx(
95
+ params: SignChainedTxParams[],
96
+ publicKeys: string[]
97
+ ): Promise<Omit<SignChainedTxResult, 'signature'>[]> {
98
+ if (params.length !== publicKeys.length) {
99
+ throw new Error(
100
+ 'The number of build chained transaction parameters must match the number of public keys provided'
101
+ )
102
+ }
103
+
104
+ const data: BuildChainedTx[] = params.map((param, index) => {
105
+ const paramType = param.type
106
+ switch (paramType) {
107
+ case 'Transfer': {
108
+ const value = this.buildTransferTxParams(param, publicKeys[index])
109
+ return { type: paramType, value }
110
+ }
111
+ case 'DeployContract': {
112
+ const value = this.buildDeployContractTxParams(param, publicKeys[index])
113
+ return { type: paramType, value }
114
+ }
115
+ case 'ExecuteScript': {
116
+ const value = this.buildExecuteScriptTxParams(param, publicKeys[index])
117
+ return { type: paramType, value }
118
+ }
119
+ default:
120
+ throw new Error(`Unsupported transaction type: ${paramType}`)
121
+ }
122
+ })
123
+
124
+ const buildChainedTxsResponse = await this.nodeProvider.transactions.postTransactionsBuildChained(data)
125
+
126
+ const results = buildChainedTxsResponse.map((buildResult) => {
127
+ const buildResultType = buildResult.type
128
+ switch (buildResultType) {
129
+ case 'Transfer': {
130
+ const buildTransferTxResult = buildResult.value
131
+ return {
132
+ ...this.convertTransferTxResult(buildTransferTxResult),
133
+ type: buildResultType
134
+ }
135
+ }
136
+ case 'DeployContract': {
137
+ const buildDeployContractTxResult = buildResult.value as BuildDeployContractTxResult
138
+ return {
139
+ ...this.convertDeployContractTxResult(buildDeployContractTxResult),
140
+ type: buildResultType
141
+ } as SignDeployContractChainedTxResult
142
+ }
143
+ case 'ExecuteScript': {
144
+ const buildExecuteScriptTxResult = buildResult.value
145
+ return {
146
+ ...this.convertExecuteScriptTxResult(buildExecuteScriptTxResult),
147
+ type: buildResultType
148
+ } as SignExecuteScriptChainedTxResult
149
+ }
150
+ default:
151
+ throw new Error(`Unexpected transaction type: ${buildResultType} for ${buildResult.value.txId}`)
152
+ }
153
+ })
154
+
155
+ return results
156
+ }
157
+
158
+ buildUnsignedTx(params: SignUnsignedTxParams): Omit<SignUnsignedTxResult, 'signature'> {
159
+ const unsignedTxBin = hexToBinUnsafe(params.unsignedTx)
160
+ const decoded = unsignedTxCodec.decode(unsignedTxBin)
161
+ const txId = binToHex(blakeHash(unsignedTxBin))
162
+ const [fromGroup, toGroup] = groupIndexOfTransaction(decoded)
163
+ return {
164
+ fromGroup: fromGroup,
165
+ toGroup: toGroup,
166
+ unsignedTx: params.unsignedTx,
167
+ txId: txId,
168
+ gasAmount: decoded.gasAmount,
169
+ gasPrice: decoded.gasPrice
170
+ }
171
+ }
172
+
173
+ private buildTransferTxParams(params: SignTransferTxParams, publicKey: string): node.BuildTransferTx {
65
174
  TransactionBuilder.validatePublicKey(params, publicKey, params.signerKeyType)
66
175
 
67
176
  const { destinations, gasPrice, ...rest } = params
68
- const data: node.BuildTransaction = {
177
+ return {
69
178
  fromPublicKey: publicKey,
70
179
  fromPublicKeyType: params.signerKeyType,
71
180
  destinations: toApiDestinations(destinations),
72
181
  gasPrice: toApiNumber256Optional(gasPrice),
73
182
  ...rest
74
183
  }
75
- const response = await this.nodeProvider.transactions.postTransactionsBuild(data)
76
- return { ...response, gasPrice: fromApiNumber256(response.gasPrice) }
77
184
  }
78
185
 
79
- async buildDeployContractTx(
186
+ private buildDeployContractTxParams(
80
187
  params: SignDeployContractTxParams,
81
188
  publicKey: string
82
- ): Promise<Omit<SignDeployContractTxResult, 'signature'>> {
189
+ ): node.BuildDeployContractTx {
83
190
  TransactionBuilder.validatePublicKey(params, publicKey, params.signerKeyType)
84
191
 
85
192
  const { initialAttoAlphAmount, initialTokenAmounts, issueTokenAmount, gasPrice, ...rest } = params
86
- const data: node.BuildDeployContractTx = {
193
+ return {
87
194
  fromPublicKey: publicKey,
88
195
  fromPublicKeyType: params.signerKeyType,
89
196
  initialAttoAlphAmount: toApiNumber256Optional(initialAttoAlphAmount),
@@ -92,19 +199,13 @@ export abstract class TransactionBuilder {
92
199
  gasPrice: toApiNumber256Optional(gasPrice),
93
200
  ...rest
94
201
  }
95
- const response = await this.nodeProvider.contracts.postContractsUnsignedTxDeployContract(data)
96
- const contractId = binToHex(contractIdFromAddress(response.contractAddress))
97
- return { ...response, groupIndex: response.fromGroup, contractId, gasPrice: fromApiNumber256(response.gasPrice) }
98
202
  }
99
203
 
100
- async buildExecuteScriptTx(
101
- params: SignExecuteScriptTxParams,
102
- publicKey: string
103
- ): Promise<Omit<SignExecuteScriptTxResult, 'signature'>> {
204
+ private buildExecuteScriptTxParams(params: SignExecuteScriptTxParams, publicKey: string): node.BuildExecuteScriptTx {
104
205
  TransactionBuilder.validatePublicKey(params, publicKey, params.signerKeyType)
105
206
 
106
207
  const { attoAlphAmount, tokens, gasPrice, ...rest } = params
107
- const data: node.BuildExecuteScriptTx = {
208
+ return {
108
209
  fromPublicKey: publicKey,
109
210
  fromPublicKeyType: params.signerKeyType,
110
211
  attoAlphAmount: toApiNumber256Optional(attoAlphAmount),
@@ -112,22 +213,34 @@ export abstract class TransactionBuilder {
112
213
  gasPrice: toApiNumber256Optional(gasPrice),
113
214
  ...rest
114
215
  }
115
- const response = await this.nodeProvider.contracts.postContractsUnsignedTxExecuteScript(data)
116
- return { ...response, groupIndex: response.fromGroup, gasPrice: fromApiNumber256(response.gasPrice) }
117
216
  }
118
217
 
119
- buildUnsignedTx(params: SignUnsignedTxParams): Omit<SignUnsignedTxResult, 'signature'> {
120
- const unsignedTxBin = hexToBinUnsafe(params.unsignedTx)
121
- const decoded = unsignedTxCodec.decode(unsignedTxBin)
122
- const txId = binToHex(blakeHash(unsignedTxBin))
123
- const [fromGroup, toGroup] = groupIndexOfTransaction(decoded)
218
+ private convertTransferTxResult(result: node.BuildTransferTxResult): Omit<SignTransferTxResult, 'signature'> {
124
219
  return {
125
- fromGroup: fromGroup,
126
- toGroup: toGroup,
127
- unsignedTx: params.unsignedTx,
128
- txId: txId,
129
- gasAmount: decoded.gasAmount,
130
- gasPrice: decoded.gasPrice
220
+ ...result,
221
+ gasPrice: fromApiNumber256(result.gasPrice)
222
+ }
223
+ }
224
+
225
+ private convertDeployContractTxResult(
226
+ result: node.BuildDeployContractTxResult
227
+ ): Omit<SignDeployContractTxResult, 'signature'> {
228
+ const contractId = binToHex(contractIdFromAddress(result.contractAddress))
229
+ return {
230
+ ...result,
231
+ groupIndex: result.fromGroup,
232
+ contractId,
233
+ gasPrice: fromApiNumber256(result.gasPrice)
234
+ }
235
+ }
236
+
237
+ private convertExecuteScriptTxResult(
238
+ result: node.BuildExecuteScriptTxResult
239
+ ): Omit<SignExecuteScriptTxResult, 'signature'> {
240
+ return {
241
+ ...result,
242
+ groupIndex: result.fromGroup,
243
+ gasPrice: fromApiNumber256(result.gasPrice)
131
244
  }
132
245
  }
133
246
  }
@@ -54,7 +54,7 @@ export interface SignTransferTxParams {
54
54
  gasAmount?: number
55
55
  gasPrice?: Number256
56
56
  }
57
- assertType<Eq<keyof SignTransferTxParams, keyof TxBuildParams<node.BuildTransaction>>>()
57
+ assertType<Eq<keyof SignTransferTxParams, keyof TxBuildParams<node.BuildTransferTx>>>()
58
58
  export interface SignTransferTxResult {
59
59
  fromGroup: number
60
60
  toGroup: number
@@ -64,7 +64,7 @@ export interface SignTransferTxResult {
64
64
  gasAmount: number
65
65
  gasPrice: Number256
66
66
  }
67
- assertType<Eq<SignTransferTxResult, SignResult<node.BuildTransactionResult>>>()
67
+ assertType<Eq<SignTransferTxResult, SignResult<node.BuildTransferTxResult>>>()
68
68
 
69
69
  export interface SignDeployContractTxParams {
70
70
  signerAddress: string
@@ -138,6 +138,26 @@ export interface SignUnsignedTxResult {
138
138
  }
139
139
  assertType<Eq<SignUnsignedTxResult, SignTransferTxResult>>
140
140
 
141
+ export type SignTransferChainedTxParams = SignTransferTxParams & { type: 'Transfer' }
142
+ export type SignDeployContractChainedTxParams = SignDeployContractTxParams & {
143
+ type: 'DeployContract'
144
+ }
145
+ export type SignExecuteScriptChainedTxParams = SignExecuteScriptTxParams & { type: 'ExecuteScript' }
146
+ export type SignChainedTxParams =
147
+ | SignTransferChainedTxParams
148
+ | SignDeployContractChainedTxParams
149
+ | SignExecuteScriptChainedTxParams
150
+
151
+ export type SignTransferChainedTxResult = SignTransferTxResult & { type: 'Transfer' }
152
+ export type SignDeployContractChainedTxResult = SignDeployContractTxResult & {
153
+ type: 'DeployContract'
154
+ }
155
+ export type SignExecuteScriptChainedTxResult = SignExecuteScriptTxResult & { type: 'ExecuteScript' }
156
+ export type SignChainedTxResult =
157
+ | SignTransferChainedTxResult
158
+ | SignDeployContractChainedTxResult
159
+ | SignExecuteScriptChainedTxResult
160
+
141
161
  export type MessageHasher =
142
162
  | 'alephium' // Message is prefixed with 'Alephium signed message: ' before hashed with blake2b
143
163
  | 'sha256'
@@ -169,3 +169,11 @@ export type Eq<X, Y> = _Eq<{ [P in keyof X]: X[P] }, { [P in keyof Y]: Y[P] }>
169
169
  // eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars
170
170
  export function assertType<T extends true>(): void {}
171
171
  export type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>
172
+
173
+ export type Narrow<type> =
174
+ | (unknown extends type ? unknown : never)
175
+ // eslint-disable-next-line @typescript-eslint/ban-types
176
+ | (type extends Function ? type : never)
177
+ | (type extends bigint | boolean | number | string ? type : never)
178
+ | (type extends [] ? [] : never)
179
+ | { [K in keyof type]: Narrow<type[K]> }