@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.
- package/dist/alephium-web3.min.js +1 -1
- package/dist/alephium-web3.min.js.map +1 -1
- package/dist/src/api/node-provider.d.ts +2 -0
- package/dist/src/api/node-provider.js +12 -6
- package/dist/src/api/utils.d.ts +1 -1
- package/dist/src/block/block.d.ts +28 -0
- package/dist/src/block/block.js +131 -0
- package/dist/src/block/index.d.ts +1 -0
- package/dist/src/block/index.js +22 -0
- package/dist/src/codec/contract-output-codec.js +4 -4
- package/dist/src/codec/lockup-script-codec.js +2 -2
- package/dist/src/codec/method-codec.d.ts +3 -1
- package/dist/src/codec/method-codec.js +27 -2
- package/dist/src/codec/script-codec.d.ts +11 -6
- package/dist/src/codec/script-codec.js +13 -2
- package/dist/src/codec/transaction-codec.js +2 -2
- package/dist/src/codec/unlock-script-codec.d.ts +2 -2
- package/dist/src/codec/unsigned-tx-codec.d.ts +2 -2
- package/dist/src/contract/contract.d.ts +19 -10
- package/dist/src/contract/contract.js +103 -56
- package/dist/src/contract/events.d.ts +1 -2
- package/dist/src/contract/events.js +28 -14
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/signer/tx-builder.js +4 -4
- package/dist/src/transaction/status.js +28 -4
- package/dist/src/utils/address.js +29 -16
- package/dist/src/utils/exchange.js +25 -15
- package/dist/src/utils/number.d.ts +1 -1
- package/dist/src/utils/sign.js +6 -6
- package/dist/src/utils/subscription.d.ts +4 -4
- package/dist/src/utils/subscription.js +1 -1
- package/package.json +3 -3
- package/src/api/node-provider.ts +8 -1
- package/src/api/utils.ts +1 -1
- package/src/block/block.ts +139 -0
- package/src/block/index.ts +19 -0
- package/src/codec/contract-output-codec.ts +1 -1
- package/src/codec/lockup-script-codec.ts +3 -3
- package/src/codec/method-codec.ts +41 -3
- package/src/codec/script-codec.ts +23 -5
- package/src/codec/transaction-codec.ts +1 -1
- package/src/codec/unlock-script-codec.ts +2 -2
- package/src/codec/unsigned-tx-codec.ts +2 -2
- package/src/contract/contract.ts +139 -78
- package/src/contract/events.ts +6 -18
- package/src/index.ts +1 -0
- package/src/signer/tx-builder.ts +2 -2
- package/src/transaction/status.ts +4 -4
- package/src/utils/address.ts +15 -2
- package/src/utils/exchange.ts +32 -10
- package/src/utils/number.ts +1 -1
- package/src/utils/sign.ts +1 -1
- package/src/utils/subscription.ts +4 -4
- package/std/fungible_token_interface.ral +1 -0
- package/std/nft_collection_interface.ral +1 -0
- package/std/nft_collection_with_royalty_interface.ral +1 -0
- package/std/nft_interface.ral +1 -0
package/src/contract/contract.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
|
535
|
-
|
|
536
|
-
|
|
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,
|
|
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
|
-
|
|
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)
|
|
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>)
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
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
|
-
|
|
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)
|
|
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>)
|
|
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
|
-
}
|
package/src/contract/events.ts
CHANGED
|
@@ -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
|
|
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
|
-
|
|
72
|
+
const subscription = new EventSubscription(options, contractAddress, fromCount)
|
|
73
|
+
subscription.subscribe()
|
|
74
|
+
return subscription
|
|
87
75
|
}
|
package/src/index.ts
CHANGED
package/src/signer/tx-builder.ts
CHANGED
|
@@ -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 {
|
|
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 =
|
|
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
|
|
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
|
-
|
|
70
|
+
const subscription = new TxStatusSubscription(options, txId, fromGroup, toGroup, confirmations)
|
|
71
|
+
subscription.subscribe()
|
|
72
|
+
return subscription
|
|
73
73
|
}
|
package/src/utils/address.ts
CHANGED
|
@@ -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
|
-
|
|
53
|
-
|
|
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
|
package/src/utils/exchange.ts
CHANGED
|
@@ -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 {
|
|
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
|
-
}
|
|
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 {
|
package/src/utils/number.ts
CHANGED
|
@@ -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'
|