@alephium/web3 0.3.0-rc.5 → 0.3.0-rc.8
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.
- package/dist/alephium-web3.min.js +1 -1
- package/dist/alephium-web3.min.js.map +1 -1
- package/dist/src/api/api-alephium.d.ts +1 -1
- package/dist/src/api/api-alephium.js +1 -1
- package/dist/src/api/api-explorer.d.ts +91 -21
- package/dist/src/api/api-explorer.js +64 -31
- package/dist/src/api/index.d.ts +0 -1
- package/dist/src/api/index.js +0 -2
- package/dist/src/contract/contract.d.ts +7 -3
- package/dist/src/contract/contract.js +84 -22
- package/dist/src/signer/index.d.ts +1 -0
- package/dist/src/signer/index.js +1 -0
- package/dist/src/signer/signer.d.ts +13 -8
- package/dist/src/signer/signer.js +17 -50
- package/dist/src/signer/tx-builder.d.ts +17 -0
- package/dist/src/signer/tx-builder.js +93 -0
- package/dist/src/signer/types.d.ts +16 -4
- package/package.json +3 -3
- package/src/api/api-alephium.ts +1 -1
- package/src/api/api-explorer.ts +134 -36
- package/src/api/index.ts +0 -2
- package/src/contract/contract.ts +108 -22
- package/src/signer/index.ts +1 -0
- package/src/signer/signer.ts +37 -63
- package/src/signer/tx-builder.ts +121 -0
- package/src/signer/types.ts +8 -1
- package/std/token_interface.ral +9 -0
package/src/contract/contract.ts
CHANGED
|
@@ -86,14 +86,32 @@ class TypedMatcher<T extends SourceKind> {
|
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
function removeParentsPrefix(parts: string[]): string {
|
|
90
|
+
let index = 0
|
|
91
|
+
for (let i = 0; i < parts.length; i++) {
|
|
92
|
+
if (parts[`${i}`] === '..') {
|
|
93
|
+
index += 1
|
|
94
|
+
} else {
|
|
95
|
+
break
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return path.join(...parts.slice(index))
|
|
99
|
+
}
|
|
100
|
+
|
|
89
101
|
class SourceInfo {
|
|
90
102
|
type: SourceKind
|
|
91
103
|
name: string
|
|
92
104
|
contractRelativePath: string
|
|
93
105
|
sourceCode: string
|
|
94
106
|
sourceCodeHash: string
|
|
107
|
+
isExternal: boolean
|
|
95
108
|
|
|
96
109
|
getArtifactPath(artifactsRootDir: string): string {
|
|
110
|
+
if (this.isExternal) {
|
|
111
|
+
const relativePath = removeParentsPrefix(this.contractRelativePath.split(path.sep))
|
|
112
|
+
const externalPath = path.join('.external', relativePath)
|
|
113
|
+
return path.join(artifactsRootDir, externalPath) + '.json'
|
|
114
|
+
}
|
|
97
115
|
return path.join(artifactsRootDir, this.contractRelativePath) + '.json'
|
|
98
116
|
}
|
|
99
117
|
|
|
@@ -102,23 +120,27 @@ class SourceInfo {
|
|
|
102
120
|
name: string,
|
|
103
121
|
sourceCode: string,
|
|
104
122
|
sourceCodeHash: string,
|
|
105
|
-
contractRelativePath: string
|
|
123
|
+
contractRelativePath: string,
|
|
124
|
+
isExternal: boolean
|
|
106
125
|
) {
|
|
107
126
|
this.type = type
|
|
108
127
|
this.name = name
|
|
109
128
|
this.sourceCode = sourceCode
|
|
110
129
|
this.sourceCodeHash = sourceCodeHash
|
|
111
130
|
this.contractRelativePath = contractRelativePath
|
|
131
|
+
this.isExternal = isExternal
|
|
112
132
|
}
|
|
113
133
|
|
|
114
134
|
static async from(
|
|
115
135
|
type: SourceKind,
|
|
116
136
|
name: string,
|
|
117
137
|
sourceCode: string,
|
|
118
|
-
contractRelativePath: string
|
|
138
|
+
contractRelativePath: string,
|
|
139
|
+
isExternal: boolean
|
|
119
140
|
): Promise<SourceInfo> {
|
|
120
141
|
const sourceCodeHash = await crypto.subtle.digest('SHA-256', Buffer.from(sourceCode))
|
|
121
|
-
|
|
142
|
+
const sourceCodeHashHex = Buffer.from(sourceCodeHash).toString('hex')
|
|
143
|
+
return new SourceInfo(type, name, sourceCode, sourceCodeHashHex, contractRelativePath, isExternal)
|
|
122
144
|
}
|
|
123
145
|
}
|
|
124
146
|
|
|
@@ -223,12 +245,13 @@ export class Project {
|
|
|
223
245
|
|
|
224
246
|
static currentProject: Project
|
|
225
247
|
|
|
248
|
+
static readonly importRegex = new RegExp('^import "[^"./]+/[^"]*[a-z][a-z_0-9]*(.ral)?"', 'mg')
|
|
226
249
|
static readonly abstractContractMatcher = new TypedMatcher<SourceKind>(
|
|
227
250
|
'^Abstract Contract ([A-Z][a-zA-Z0-9]*)',
|
|
228
251
|
SourceKind.AbstractContract
|
|
229
252
|
)
|
|
230
253
|
static readonly contractMatcher = new TypedMatcher('^Contract ([A-Z][a-zA-Z0-9]*)', SourceKind.Contract)
|
|
231
|
-
static readonly interfaceMatcher = new TypedMatcher('^Interface ([A-Z][a-zA-Z0-9]*)
|
|
254
|
+
static readonly interfaceMatcher = new TypedMatcher('^Interface ([A-Z][a-zA-Z0-9]*)', SourceKind.Interface)
|
|
232
255
|
static readonly scriptMatcher = new TypedMatcher('^TxScript ([A-Z][a-zA-Z0-9]*)', SourceKind.Script)
|
|
233
256
|
static readonly matchers = [
|
|
234
257
|
Project.abstractContractMatcher,
|
|
@@ -445,45 +468,107 @@ export class Project {
|
|
|
445
468
|
}
|
|
446
469
|
}
|
|
447
470
|
|
|
471
|
+
private static getImportSourcePath(projectRootDir: string, importPath: string): string {
|
|
472
|
+
const parts = importPath.split(path.sep)
|
|
473
|
+
if (parts.length > 1 && parts[0] === 'std') {
|
|
474
|
+
const currentDir = path.dirname(__filename)
|
|
475
|
+
return path.join(...[currentDir, '..', '..', '..', importPath])
|
|
476
|
+
}
|
|
477
|
+
let moduleDir = projectRootDir
|
|
478
|
+
while (true) {
|
|
479
|
+
const expectedPath = path.join(...[moduleDir, 'node_modules', importPath])
|
|
480
|
+
if (fs.existsSync(expectedPath)) {
|
|
481
|
+
return expectedPath
|
|
482
|
+
}
|
|
483
|
+
const oldModuleDir = moduleDir
|
|
484
|
+
moduleDir = path.join(moduleDir, '..')
|
|
485
|
+
if (oldModuleDir === moduleDir) {
|
|
486
|
+
throw new Error(`Specified import file does not exist: ${importPath}`)
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
private static async handleImports(
|
|
492
|
+
projectRootDir: string,
|
|
493
|
+
contractRootDir: string,
|
|
494
|
+
sourceStr: string,
|
|
495
|
+
importsCache: string[]
|
|
496
|
+
): Promise<[string, SourceInfo[]]> {
|
|
497
|
+
const localImportsCache: string[] = []
|
|
498
|
+
const result = sourceStr.replace(Project.importRegex, (match) => {
|
|
499
|
+
localImportsCache.push(match)
|
|
500
|
+
return ''
|
|
501
|
+
})
|
|
502
|
+
const externalSourceInfos: SourceInfo[] = []
|
|
503
|
+
for (const myImport of localImportsCache) {
|
|
504
|
+
const originImportPath = myImport.slice(8, -1)
|
|
505
|
+
const importPath = originImportPath.endsWith('.ral') ? originImportPath : originImportPath + '.ral'
|
|
506
|
+
if (!importsCache.includes(importPath)) {
|
|
507
|
+
importsCache.push(importPath)
|
|
508
|
+
const sourcePath = Project.getImportSourcePath(projectRootDir, importPath)
|
|
509
|
+
const sourceInfos = await Project.loadSourceFile(
|
|
510
|
+
projectRootDir,
|
|
511
|
+
contractRootDir,
|
|
512
|
+
sourcePath,
|
|
513
|
+
importsCache,
|
|
514
|
+
true
|
|
515
|
+
)
|
|
516
|
+
externalSourceInfos.push(...sourceInfos)
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
return [result, externalSourceInfos]
|
|
520
|
+
}
|
|
521
|
+
|
|
448
522
|
private static async loadSourceFile(
|
|
523
|
+
projectRootDir: string,
|
|
449
524
|
contractsRootDir: string,
|
|
450
|
-
|
|
451
|
-
|
|
525
|
+
sourcePath: string,
|
|
526
|
+
importsCache: string[],
|
|
527
|
+
isExternal: boolean
|
|
452
528
|
): Promise<SourceInfo[]> {
|
|
453
|
-
const
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
throw new Error(`Invalid filename: ${contractPath}, smart contract file name should end with ".ral"`)
|
|
529
|
+
const contractRelativePath = path.relative(contractsRootDir, sourcePath)
|
|
530
|
+
if (!sourcePath.endsWith('.ral')) {
|
|
531
|
+
throw new Error(`Invalid filename: ${sourcePath}, smart contract file name should end with ".ral"`)
|
|
457
532
|
}
|
|
458
533
|
|
|
459
|
-
const sourceBuffer = await fsPromises.readFile(
|
|
460
|
-
const sourceStr =
|
|
461
|
-
|
|
534
|
+
const sourceBuffer = await fsPromises.readFile(sourcePath)
|
|
535
|
+
const [sourceStr, externalSourceInfos] = await Project.handleImports(
|
|
536
|
+
projectRootDir,
|
|
537
|
+
contractsRootDir,
|
|
538
|
+
sourceBuffer.toString(),
|
|
539
|
+
importsCache
|
|
540
|
+
)
|
|
541
|
+
if (sourceStr.match(new RegExp('^import "', 'mg')) !== null) {
|
|
542
|
+
throw new Error(`Invalid import statements, source: ${sourcePath}`)
|
|
543
|
+
}
|
|
544
|
+
const sourceInfos = externalSourceInfos
|
|
462
545
|
for (const matcher of this.matchers) {
|
|
463
546
|
const results = sourceStr.matchAll(matcher.matcher)
|
|
464
547
|
for (const result of results) {
|
|
465
|
-
const sourceInfo = await SourceInfo.from(matcher.type, result[1], sourceStr, contractRelativePath)
|
|
548
|
+
const sourceInfo = await SourceInfo.from(matcher.type, result[1], sourceStr, contractRelativePath, isExternal)
|
|
466
549
|
sourceInfos.push(sourceInfo)
|
|
467
550
|
}
|
|
468
551
|
}
|
|
469
552
|
return sourceInfos
|
|
470
553
|
}
|
|
471
554
|
|
|
472
|
-
private static async loadSourceFiles(contractsRootDir: string): Promise<SourceInfo[]> {
|
|
473
|
-
const
|
|
555
|
+
private static async loadSourceFiles(projectRootDir: string, contractsRootDir: string): Promise<SourceInfo[]> {
|
|
556
|
+
const importsCache: string[] = []
|
|
557
|
+
const sourceInfos: SourceInfo[] = []
|
|
558
|
+
const loadDir = async function (dirPath: string): Promise<void> {
|
|
474
559
|
const dirents = await fsPromises.readdir(dirPath, { withFileTypes: true })
|
|
475
560
|
for (const dirent of dirents) {
|
|
476
561
|
if (dirent.isFile()) {
|
|
477
|
-
const
|
|
478
|
-
|
|
562
|
+
const sourcePath = path.join(dirPath, dirent.name)
|
|
563
|
+
const infos = await Project.loadSourceFile(projectRootDir, contractsRootDir, sourcePath, importsCache, false)
|
|
564
|
+
sourceInfos.push(...infos)
|
|
479
565
|
} else {
|
|
480
566
|
const newPath = path.join(dirPath, dirent.name)
|
|
481
|
-
await loadDir(newPath
|
|
567
|
+
await loadDir(newPath)
|
|
482
568
|
}
|
|
483
569
|
}
|
|
484
570
|
}
|
|
485
|
-
|
|
486
|
-
await loadDir(contractsRootDir, sourceInfos)
|
|
571
|
+
await loadDir(contractsRootDir)
|
|
487
572
|
const contractAndScriptSize = sourceInfos.filter(
|
|
488
573
|
(f) => f.type === SourceKind.Contract || f.type === SourceKind.Script
|
|
489
574
|
).length
|
|
@@ -498,11 +583,12 @@ export class Project {
|
|
|
498
583
|
|
|
499
584
|
static async build(
|
|
500
585
|
compilerOptionsPartial: Partial<CompilerOptions> = {},
|
|
586
|
+
projectRootDir = '.',
|
|
501
587
|
contractsRootDir = Project.DEFAULT_CONTRACTS_DIR,
|
|
502
588
|
artifactsRootDir = Project.DEFAULT_ARTIFACTS_DIR
|
|
503
589
|
): Promise<void> {
|
|
504
590
|
const provider = getCurrentNodeProvider()
|
|
505
|
-
const sourceFiles = await Project.loadSourceFiles(contractsRootDir)
|
|
591
|
+
const sourceFiles = await Project.loadSourceFiles(projectRootDir, contractsRootDir)
|
|
506
592
|
const { errorOnWarnings, ...nodeCompilerOptions } = { ...DEFAULT_COMPILER_OPTIONS, ...compilerOptionsPartial }
|
|
507
593
|
const projectArtifact = await ProjectArtifact.from(artifactsRootDir)
|
|
508
594
|
if (typeof projectArtifact === 'undefined' || projectArtifact.needToReCompile(nodeCompilerOptions, sourceFiles)) {
|
package/src/signer/index.ts
CHANGED
package/src/signer/signer.ts
CHANGED
|
@@ -17,15 +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 {
|
|
21
|
-
ExplorerProvider,
|
|
22
|
-
fromApiNumber256,
|
|
23
|
-
fromApiTokens,
|
|
24
|
-
NodeProvider,
|
|
25
|
-
toApiNumber256,
|
|
26
|
-
toApiNumber256Optional,
|
|
27
|
-
toApiTokens
|
|
28
|
-
} from '../api'
|
|
20
|
+
import { ExplorerProvider, fromApiNumber256, fromApiTokens, NodeProvider, toApiNumber256, toApiTokens } from '../api'
|
|
29
21
|
import { node } from '../api'
|
|
30
22
|
import * as utils from '../utils'
|
|
31
23
|
import blake from 'blakejs'
|
|
@@ -46,8 +38,14 @@ import {
|
|
|
46
38
|
SignUnsignedTxParams,
|
|
47
39
|
SignUnsignedTxResult,
|
|
48
40
|
SubmissionResult,
|
|
49
|
-
SubmitTransactionParams
|
|
41
|
+
SubmitTransactionParams,
|
|
42
|
+
ExtSignTransferTxParams,
|
|
43
|
+
ExtSignDeployContractTxParams,
|
|
44
|
+
ExtSignExecuteScriptTxParams,
|
|
45
|
+
ExtSignUnsignedTxParams,
|
|
46
|
+
ExtSignMessageParams
|
|
50
47
|
} from './types'
|
|
48
|
+
import { TransactionBuilder } from './tx-builder'
|
|
51
49
|
|
|
52
50
|
const ec = new EC('secp256k1')
|
|
53
51
|
|
|
@@ -71,12 +69,19 @@ export interface SignerProvider {
|
|
|
71
69
|
// Abstraction for interactive signer (e.g. WalletConnect instance, Extension wallet object)
|
|
72
70
|
export interface InteractiveSignerProvider<EnableOptions extends EnableOptionsBase = EnableOptionsBase>
|
|
73
71
|
extends SignerProvider {
|
|
74
|
-
enable(opt?: EnableOptions): Promise<
|
|
72
|
+
enable(opt?: EnableOptions): Promise<Address>
|
|
75
73
|
disconnect(): Promise<void>
|
|
74
|
+
|
|
75
|
+
// Methods inherited from SignerProvider, but require networkId in the params
|
|
76
|
+
signAndSubmitTransferTx(params: ExtSignTransferTxParams): Promise<SignTransferTxResult>
|
|
77
|
+
signAndSubmitDeployContractTx(params: ExtSignDeployContractTxParams): Promise<SignDeployContractTxResult>
|
|
78
|
+
signAndSubmitExecuteScriptTx(params: ExtSignExecuteScriptTxParams): Promise<SignExecuteScriptTxResult>
|
|
79
|
+
signAndSubmitUnsignedTx(params: ExtSignUnsignedTxParams): Promise<SignUnsignedTxResult>
|
|
80
|
+
signUnsignedTx(params: ExtSignUnsignedTxParams): Promise<SignUnsignedTxResult>
|
|
81
|
+
signMessage(params: ExtSignMessageParams): Promise<SignMessageResult>
|
|
76
82
|
}
|
|
77
83
|
|
|
78
|
-
export abstract class SignerProviderSimple implements SignerProvider {
|
|
79
|
-
abstract get nodeProvider(): NodeProvider | undefined
|
|
84
|
+
export abstract class SignerProviderSimple extends TransactionBuilder implements SignerProvider {
|
|
80
85
|
abstract get explorerProvider(): ExplorerProvider | undefined
|
|
81
86
|
|
|
82
87
|
abstract getSelectedAccount(): Promise<Account>
|
|
@@ -86,16 +91,9 @@ export abstract class SignerProviderSimple implements SignerProvider {
|
|
|
86
91
|
return account.address
|
|
87
92
|
}
|
|
88
93
|
|
|
89
|
-
private getNodeProvider(): NodeProvider {
|
|
90
|
-
if (this.nodeProvider === undefined) {
|
|
91
|
-
throw Error('The signer does not contain a node provider')
|
|
92
|
-
}
|
|
93
|
-
return this.nodeProvider
|
|
94
|
-
}
|
|
95
|
-
|
|
96
94
|
async submitTransaction(params: SubmitTransactionParams): Promise<SubmissionResult> {
|
|
97
95
|
const data: node.SubmitTransaction = { unsignedTx: params.unsignedTx, signature: params.signature }
|
|
98
|
-
return this.
|
|
96
|
+
return this.nodeProvider.transactions.postTransactionsSubmit(data)
|
|
99
97
|
}
|
|
100
98
|
|
|
101
99
|
async signAndSubmitTransferTx(params: SignTransferTxParams): Promise<SignTransferTxResult> {
|
|
@@ -132,67 +130,43 @@ export abstract class SignerProviderSimple implements SignerProvider {
|
|
|
132
130
|
async signTransferTx(params: SignTransferTxParams): Promise<SignTransferTxResult> {
|
|
133
131
|
const response = await this.buildTransferTx(params)
|
|
134
132
|
const signature = await this.signRaw(params.signerAddress, response.txId)
|
|
135
|
-
return {
|
|
133
|
+
return { signature, ...response }
|
|
136
134
|
}
|
|
137
135
|
|
|
138
|
-
async buildTransferTx(params: SignTransferTxParams): Promise<
|
|
139
|
-
|
|
140
|
-
...(await this.usePublicKey(params)),
|
|
141
|
-
destinations: toApiDestinations(params.destinations),
|
|
142
|
-
gasPrice: toApiNumber256Optional(params.gasPrice)
|
|
143
|
-
}
|
|
144
|
-
return this.getNodeProvider().transactions.postTransactionsBuild(data)
|
|
136
|
+
override async buildTransferTx(params: SignTransferTxParams): Promise<Omit<SignTransferTxResult, 'signature'>> {
|
|
137
|
+
return super.buildTransferTx(params, await this.getPublicKey(params.signerAddress))
|
|
145
138
|
}
|
|
146
139
|
|
|
147
140
|
async signDeployContractTx(params: SignDeployContractTxParams): Promise<SignDeployContractTxResult> {
|
|
148
|
-
const response = await this.
|
|
141
|
+
const response = await this.buildDeployContractTx(params)
|
|
149
142
|
const signature = await this.signRaw(params.signerAddress, response.txId)
|
|
150
|
-
|
|
151
|
-
return { ...response, contractId, signature, gasPrice: fromApiNumber256(response.gasPrice) }
|
|
143
|
+
return { signature, ...response }
|
|
152
144
|
}
|
|
153
145
|
|
|
154
|
-
async
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
initialTokenAmounts: toApiTokens(params.initialTokenAmounts),
|
|
159
|
-
issueTokenAmount: toApiNumber256Optional(params.issueTokenAmount),
|
|
160
|
-
gasPrice: toApiNumber256Optional(params.gasPrice)
|
|
161
|
-
}
|
|
162
|
-
return this.getNodeProvider().contracts.postContractsUnsignedTxDeployContract(data)
|
|
146
|
+
override async buildDeployContractTx(
|
|
147
|
+
params: SignDeployContractTxParams
|
|
148
|
+
): Promise<Omit<SignDeployContractTxResult, 'signature'>> {
|
|
149
|
+
return super.buildDeployContractTx(params, await this.getPublicKey(params.signerAddress))
|
|
163
150
|
}
|
|
164
151
|
|
|
165
152
|
async signExecuteScriptTx(params: SignExecuteScriptTxParams): Promise<SignExecuteScriptTxResult> {
|
|
166
|
-
const response = await this.
|
|
153
|
+
const response = await this.buildExecuteScriptTx(params)
|
|
167
154
|
const signature = await this.signRaw(params.signerAddress, response.txId)
|
|
168
|
-
return {
|
|
155
|
+
return { signature, ...response }
|
|
169
156
|
}
|
|
170
157
|
|
|
171
|
-
async
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
tokens: toApiTokens(params.tokens),
|
|
176
|
-
gasPrice: toApiNumber256Optional(params.gasPrice)
|
|
177
|
-
}
|
|
178
|
-
return this.getNodeProvider().contracts.postContractsUnsignedTxExecuteScript(data)
|
|
158
|
+
override async buildExecuteScriptTx(
|
|
159
|
+
params: SignExecuteScriptTxParams
|
|
160
|
+
): Promise<Omit<SignExecuteScriptTxResult, 'signature'>> {
|
|
161
|
+
return super.buildExecuteScriptTx(params, await this.getPublicKey(params.signerAddress))
|
|
179
162
|
}
|
|
180
163
|
|
|
181
164
|
// in general, wallet should show the decoded information to user for confirmation
|
|
182
165
|
// please overwrite this function for real wallet
|
|
183
166
|
async signUnsignedTx(params: SignUnsignedTxParams): Promise<SignUnsignedTxResult> {
|
|
184
|
-
const
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
return {
|
|
188
|
-
fromGroup: decoded.fromGroup,
|
|
189
|
-
toGroup: decoded.toGroup,
|
|
190
|
-
unsignedTx: params.unsignedTx,
|
|
191
|
-
txId: decoded.unsignedTx.txId,
|
|
192
|
-
signature,
|
|
193
|
-
gasAmount: decoded.unsignedTx.gasAmount,
|
|
194
|
-
gasPrice: fromApiNumber256(decoded.unsignedTx.gasPrice)
|
|
195
|
-
}
|
|
167
|
+
const response = await this.buildUnsignedTx(params)
|
|
168
|
+
const signature = await this.signRaw(params.signerAddress, response.txId)
|
|
169
|
+
return { signature, ...response }
|
|
196
170
|
}
|
|
197
171
|
|
|
198
172
|
async signMessage(params: SignMessageParams): Promise<SignMessageResult> {
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2018 - 2022 The Alephium Authors
|
|
3
|
+
This file is part of the alephium project.
|
|
4
|
+
|
|
5
|
+
The library is free software: you can redistribute it and/or modify
|
|
6
|
+
it under the terms of the GNU Lesser General Public License as published by
|
|
7
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
(at your option) any later version.
|
|
9
|
+
|
|
10
|
+
The library is distributed in the hope that it will be useful,
|
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
GNU Lesser General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU Lesser General Public License
|
|
16
|
+
along with the library. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { utils } from '..'
|
|
20
|
+
import { fromApiNumber256, node, NodeProvider, toApiNumber256Optional, toApiTokens } from '../api'
|
|
21
|
+
import { addressFromPublicKey } from '../utils'
|
|
22
|
+
import { toApiDestinations } from './signer'
|
|
23
|
+
import {
|
|
24
|
+
SignDeployContractTxParams,
|
|
25
|
+
SignDeployContractTxResult,
|
|
26
|
+
SignerAddress,
|
|
27
|
+
SignExecuteScriptTxParams,
|
|
28
|
+
SignExecuteScriptTxResult,
|
|
29
|
+
SignTransferTxParams,
|
|
30
|
+
SignTransferTxResult,
|
|
31
|
+
SignUnsignedTxParams,
|
|
32
|
+
SignUnsignedTxResult
|
|
33
|
+
} from './types'
|
|
34
|
+
|
|
35
|
+
export abstract class TransactionBuilder {
|
|
36
|
+
abstract get nodeProvider(): NodeProvider
|
|
37
|
+
|
|
38
|
+
static create(baseUrl: string, apiKey?: string) {
|
|
39
|
+
const nodeProvider = new NodeProvider(baseUrl, apiKey)
|
|
40
|
+
return new (class extends TransactionBuilder {
|
|
41
|
+
get nodeProvider(): NodeProvider {
|
|
42
|
+
return nodeProvider
|
|
43
|
+
}
|
|
44
|
+
})()
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private static validatePublicKey(params: SignerAddress, publicKey: string) {
|
|
48
|
+
const address = addressFromPublicKey(publicKey)
|
|
49
|
+
if (address !== params.signerAddress) {
|
|
50
|
+
throw new Error('Unmatched public key')
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async buildTransferTx(
|
|
55
|
+
params: SignTransferTxParams,
|
|
56
|
+
publicKey: string
|
|
57
|
+
): Promise<Omit<SignTransferTxResult, 'signature'>> {
|
|
58
|
+
TransactionBuilder.validatePublicKey(params, publicKey)
|
|
59
|
+
|
|
60
|
+
const { destinations, gasPrice, ...rest } = params
|
|
61
|
+
const data: node.BuildTransaction = {
|
|
62
|
+
fromPublicKey: publicKey,
|
|
63
|
+
destinations: toApiDestinations(destinations),
|
|
64
|
+
gasPrice: toApiNumber256Optional(gasPrice),
|
|
65
|
+
...rest
|
|
66
|
+
}
|
|
67
|
+
const response = await this.nodeProvider.transactions.postTransactionsBuild(data)
|
|
68
|
+
return { ...response, gasPrice: fromApiNumber256(response.gasPrice) }
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async buildDeployContractTx(
|
|
72
|
+
params: SignDeployContractTxParams,
|
|
73
|
+
publicKey: string
|
|
74
|
+
): Promise<Omit<SignDeployContractTxResult, 'signature'>> {
|
|
75
|
+
TransactionBuilder.validatePublicKey(params, publicKey)
|
|
76
|
+
|
|
77
|
+
const { initialAttoAlphAmount, initialTokenAmounts, issueTokenAmount, gasPrice, ...rest } = params
|
|
78
|
+
const data: node.BuildDeployContractTx = {
|
|
79
|
+
fromPublicKey: publicKey,
|
|
80
|
+
initialAttoAlphAmount: toApiNumber256Optional(initialAttoAlphAmount),
|
|
81
|
+
initialTokenAmounts: toApiTokens(initialTokenAmounts),
|
|
82
|
+
issueTokenAmount: toApiNumber256Optional(issueTokenAmount),
|
|
83
|
+
gasPrice: toApiNumber256Optional(gasPrice),
|
|
84
|
+
...rest
|
|
85
|
+
}
|
|
86
|
+
const response = await this.nodeProvider.contracts.postContractsUnsignedTxDeployContract(data)
|
|
87
|
+
const contractId = utils.binToHex(utils.contractIdFromAddress(response.contractAddress))
|
|
88
|
+
return { ...response, contractId, gasPrice: fromApiNumber256(response.gasPrice) }
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async buildExecuteScriptTx(
|
|
92
|
+
params: SignExecuteScriptTxParams,
|
|
93
|
+
publicKey: string
|
|
94
|
+
): Promise<Omit<SignExecuteScriptTxResult, 'signature'>> {
|
|
95
|
+
TransactionBuilder.validatePublicKey(params, publicKey)
|
|
96
|
+
|
|
97
|
+
const { attoAlphAmount, tokens, gasPrice, ...rest } = params
|
|
98
|
+
const data: node.BuildExecuteScriptTx = {
|
|
99
|
+
fromPublicKey: publicKey,
|
|
100
|
+
attoAlphAmount: toApiNumber256Optional(attoAlphAmount),
|
|
101
|
+
tokens: toApiTokens(tokens),
|
|
102
|
+
gasPrice: toApiNumber256Optional(gasPrice),
|
|
103
|
+
...rest
|
|
104
|
+
}
|
|
105
|
+
const response = await this.nodeProvider.contracts.postContractsUnsignedTxExecuteScript(data)
|
|
106
|
+
return { ...response, gasPrice: fromApiNumber256(response.gasPrice) }
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async buildUnsignedTx(params: SignUnsignedTxParams): Promise<Omit<SignUnsignedTxResult, 'signature'>> {
|
|
110
|
+
const data = { unsignedTx: params.unsignedTx }
|
|
111
|
+
const decoded = await this.nodeProvider.transactions.postTransactionsDecodeUnsignedTx(data)
|
|
112
|
+
return {
|
|
113
|
+
fromGroup: decoded.fromGroup,
|
|
114
|
+
toGroup: decoded.toGroup,
|
|
115
|
+
unsignedTx: params.unsignedTx,
|
|
116
|
+
txId: decoded.unsignedTx.txId,
|
|
117
|
+
gasAmount: decoded.unsignedTx.gasAmount,
|
|
118
|
+
gasPrice: fromApiNumber256(decoded.unsignedTx.gasPrice)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
package/src/signer/types.ts
CHANGED
|
@@ -143,6 +143,13 @@ export interface SubmissionResult {
|
|
|
143
143
|
export interface EnableOptionsBase {
|
|
144
144
|
// chainGroup - specify whether to use addresses from a specific group
|
|
145
145
|
chainGroup?: number
|
|
146
|
+
networkId: string
|
|
146
147
|
onDisconnected: () => Promise<void>
|
|
147
|
-
onNetworkChanged: (network: { networkName: string; networkId: number }) => Promise<void>
|
|
148
148
|
}
|
|
149
|
+
|
|
150
|
+
// Transaction Params for InteractiveSignerProvider
|
|
151
|
+
export type ExtSignTransferTxParams = SignTransferTxParams & { networkId: string }
|
|
152
|
+
export type ExtSignDeployContractTxParams = SignDeployContractTxParams & { networkId: string }
|
|
153
|
+
export type ExtSignExecuteScriptTxParams = SignExecuteScriptTxParams & { networkId: string }
|
|
154
|
+
export type ExtSignUnsignedTxParams = SignUnsignedTxParams & { networkId: string }
|
|
155
|
+
export type ExtSignMessageParams = SignMessageParams & { networkId: string }
|