@alephium/web3 0.43.0 → 0.45.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.
@@ -17,7 +17,7 @@ along with the library. If not, see <http://www.gnu.org/licenses/>.
17
17
  */
18
18
  import { Parser } from 'binary-parser'
19
19
  import { ArrayCodec, DecodedArray } from './array-codec'
20
- import { DecodedCompactInt, compactUnsignedIntCodec } from './compact-int-codec'
20
+ import { DecodedCompactInt, compactSignedIntCodec } from './compact-int-codec'
21
21
  import { Codec } from './codec'
22
22
  import { instrsCodec, Instr } from './instr-codec'
23
23
 
@@ -82,13 +82,13 @@ export class MethodCodec implements Codec<DecodedMethod> {
82
82
  .uint8('isPublic')
83
83
  .uint8('assetModifier')
84
84
  .nest('argsLength', {
85
- type: compactUnsignedIntCodec.parser
85
+ type: compactSignedIntCodec.parser
86
86
  })
87
87
  .nest('localsLength', {
88
- type: compactUnsignedIntCodec.parser
88
+ type: compactSignedIntCodec.parser
89
89
  })
90
90
  .nest('returnLength', {
91
- type: compactUnsignedIntCodec.parser
91
+ type: compactSignedIntCodec.parser
92
92
  })
93
93
  .nest('instrs', {
94
94
  type: instrsCodec.parser
@@ -96,9 +96,9 @@ export class MethodCodec implements Codec<DecodedMethod> {
96
96
 
97
97
  encode(input: DecodedMethod): Uint8Array {
98
98
  const result = [input.isPublic, input.assetModifier]
99
- result.push(...compactUnsignedIntCodec.encode(input.argsLength))
100
- result.push(...compactUnsignedIntCodec.encode(input.localsLength))
101
- result.push(...compactUnsignedIntCodec.encode(input.returnLength))
99
+ result.push(...compactSignedIntCodec.encode(input.argsLength))
100
+ result.push(...compactSignedIntCodec.encode(input.localsLength))
101
+ result.push(...compactSignedIntCodec.encode(input.returnLength))
102
102
  result.push(...instrsCodec.encode(input.instrs.value))
103
103
  return new Uint8Array(result)
104
104
  }
@@ -111,9 +111,9 @@ export class MethodCodec implements Codec<DecodedMethod> {
111
111
  return {
112
112
  isPublic: decodedMethod.isPublic === 1,
113
113
  ...decodeAssetModifier(decodedMethod.assetModifier),
114
- argsLength: compactUnsignedIntCodec.toU32(decodedMethod.argsLength),
115
- localsLength: compactUnsignedIntCodec.toU32(decodedMethod.localsLength),
116
- returnLength: compactUnsignedIntCodec.toU32(decodedMethod.returnLength),
114
+ argsLength: compactSignedIntCodec.toI32(decodedMethod.argsLength),
115
+ localsLength: compactSignedIntCodec.toI32(decodedMethod.localsLength),
116
+ returnLength: compactSignedIntCodec.toI32(decodedMethod.returnLength),
117
117
  instrs: decodedMethod.instrs.value
118
118
  }
119
119
  }
@@ -122,9 +122,9 @@ export class MethodCodec implements Codec<DecodedMethod> {
122
122
  return {
123
123
  isPublic: method.isPublic ? 1 : 0,
124
124
  assetModifier: encodeAssetModifier(method),
125
- argsLength: compactUnsignedIntCodec.fromU32(method.argsLength),
126
- localsLength: compactUnsignedIntCodec.fromU32(method.localsLength),
127
- returnLength: compactUnsignedIntCodec.fromU32(method.returnLength),
125
+ argsLength: compactSignedIntCodec.fromI32(method.argsLength),
126
+ localsLength: compactSignedIntCodec.fromI32(method.localsLength),
127
+ returnLength: compactSignedIntCodec.fromI32(method.returnLength),
128
128
  instrs: instrsCodec.fromArray(method.instrs)
129
129
  }
130
130
  }
@@ -21,7 +21,7 @@ import { DecodedArray } from './array-codec'
21
21
  import { Codec } from './codec'
22
22
  import { DecodedMethod, methodsCodec, Method, MethodCodec } from './method-codec'
23
23
  import { OptionCodec } from './option-codec'
24
- import { compactUnsignedIntCodec } from './compact-int-codec'
24
+ import { compactSignedIntCodec } from './compact-int-codec'
25
25
 
26
26
  export interface DecodedScript {
27
27
  methods: DecodedArray<DecodedMethod>
@@ -51,7 +51,7 @@ export class ScriptCodec implements Codec<DecodedScript> {
51
51
  }
52
52
 
53
53
  encodeScript(inputTxScript: Script): Uint8Array {
54
- const methodLength = compactUnsignedIntCodec.fromU32(inputTxScript.methods.length)
54
+ const methodLength = compactSignedIntCodec.fromI32(inputTxScript.methods.length)
55
55
  const decodedMethods = inputTxScript.methods.map((method) => MethodCodec.fromMethod(method))
56
56
  return this.encode({ methods: { value: decodedMethods, length: methodLength } })
57
57
  }
@@ -40,7 +40,8 @@ import {
40
40
  SignDeployContractTxResult,
41
41
  SignExecuteScriptTxParams,
42
42
  SignerProvider,
43
- Address
43
+ Address,
44
+ SignExecuteScriptTxResult
44
45
  } from '../signer'
45
46
  import * as ralph from './ralph'
46
47
  import {
@@ -63,6 +64,8 @@ import * as blake from 'blakejs'
63
64
  import { isContractDebugMessageEnabled } from '../debug'
64
65
  import {
65
66
  contract,
67
+ compactUnsignedIntCodec,
68
+ compactSignedIntCodec,
66
69
  Method,
67
70
  LoadLocal,
68
71
  LoadImmFieldByIndex,
@@ -72,7 +75,19 @@ import {
72
75
  ByteVecEq,
73
76
  Assert,
74
77
  StoreMutFieldByIndex,
75
- DestroySelf
78
+ DestroySelf,
79
+ Pop,
80
+ byteStringCodec,
81
+ StoreLocal,
82
+ instrCodec,
83
+ U256Const,
84
+ Instr,
85
+ ApproveToken,
86
+ ApproveAlph,
87
+ CallExternal,
88
+ Dup,
89
+ CallerAddress,
90
+ ByteConst
76
91
  } from '../codec'
77
92
 
78
93
  const crypto = new WebCrypto()
@@ -1043,6 +1058,15 @@ export interface CallContractResult<R> {
1043
1058
  debugMessages: DebugMessage[]
1044
1059
  }
1045
1060
 
1061
+ export interface SignExecuteContractMethodParams<T extends Arguments = Arguments> {
1062
+ args: T
1063
+ signer: SignerProvider
1064
+ attoAlphAmount?: Number256
1065
+ tokens?: Token[]
1066
+ gasAmount?: number
1067
+ gasPrice?: Number256
1068
+ }
1069
+
1046
1070
  function specialContractAddress(eventIndex: number, groupIndex: number): string {
1047
1071
  const bytes = new Uint8Array(32).fill(0)
1048
1072
  bytes[30] = eventIndex
@@ -1606,6 +1630,251 @@ export async function callMethod<I extends ContractInstance, F extends Fields, A
1606
1630
  return callResult as CallContractResult<R>
1607
1631
  }
1608
1632
 
1633
+ export async function signExecuteMethod<I extends ContractInstance, F extends Fields, A extends Arguments, R>(
1634
+ contract: ContractFactory<I, F>,
1635
+ instance: ContractInstance,
1636
+ methodName: string,
1637
+ params: Optional<SignExecuteContractMethodParams<A>, 'args'>
1638
+ ): Promise<SignExecuteScriptTxResult> {
1639
+ const methodIndex = contract.contract.getMethodIndex(methodName)
1640
+ const functionSig = contract.contract.functions[methodIndex]
1641
+ const usePreapprovedAssets = contract.contract.decodedMethods[methodIndex].usePreapprovedAssets
1642
+ const bytecodeTemplate = getBytecodeTemplate(
1643
+ methodIndex,
1644
+ usePreapprovedAssets,
1645
+ functionSig,
1646
+ contract.contract.structs,
1647
+ params.attoAlphAmount,
1648
+ params.tokens
1649
+ )
1650
+
1651
+ const fieldsSig = toFieldsSig(contract.contract.name, functionSig)
1652
+ const bytecode = ralph.buildScriptByteCode(
1653
+ bytecodeTemplate,
1654
+ { __contract__: instance.contractId, ...params.args },
1655
+ fieldsSig,
1656
+ contract.contract.structs
1657
+ )
1658
+
1659
+ const signer = params.signer
1660
+ const selectedAccount = await signer.getSelectedAccount()
1661
+ const signerParams: SignExecuteScriptTxParams = {
1662
+ signerAddress: selectedAccount.address,
1663
+ signerKeyType: selectedAccount.keyType,
1664
+ bytecode: bytecode,
1665
+ attoAlphAmount: params.attoAlphAmount,
1666
+ tokens: params.tokens,
1667
+ gasAmount: params.gasAmount,
1668
+ gasPrice: params.gasPrice
1669
+ }
1670
+
1671
+ return await signer.signAndSubmitExecuteScriptTx(signerParams)
1672
+ }
1673
+
1674
+ function getBytecodeTemplate(
1675
+ methodIndex: number,
1676
+ usePreapprovedAssets: boolean,
1677
+ functionSig: FunctionSig,
1678
+ structs: Struct[],
1679
+ attoAlphAmount?: Number256,
1680
+ tokens?: Token[]
1681
+ ): string {
1682
+ // For the default TxScript main function
1683
+ const numberOfMethods = '01'
1684
+ const isPublic = '01'
1685
+ const modifier = usePreapprovedAssets ? '03' : '00'
1686
+ const argsLength = '00'
1687
+ const returnsLength = '00'
1688
+
1689
+ const [templateVarStoreLocalInstrs, templateVarsLength] = getTemplateVarStoreLocalInstrs(functionSig, structs)
1690
+
1691
+ const approveAlphInstrs: string[] = getApproveAlphInstrs(usePreapprovedAssets ? attoAlphAmount : undefined)
1692
+ const approveTokensInstrs: string[] = getApproveTokensInstrs(usePreapprovedAssets ? tokens : undefined)
1693
+ const callerInstrs: string[] = getCallAddressInstrs(approveAlphInstrs.length / 2 + approveTokensInstrs.length / 3)
1694
+
1695
+ // First template var is the contract
1696
+ const functionArgsNum = encodeU256Const(BigInt(templateVarsLength - 1))
1697
+ const localsLength = encodeI32(templateVarStoreLocalInstrs.length / 2)
1698
+
1699
+ const templateVarLoadLocalInstrs = getTemplateVarLoadLocalInstrs(functionSig, structs)
1700
+
1701
+ const functionReturnTypesLength: number = functionSig.returnTypes.reduce(
1702
+ (acc, returnType) => acc + ralph.typeLength(returnType, structs),
1703
+ 0
1704
+ )
1705
+ const functionReturnPopInstrs = encodeInstr(Pop).repeat(functionReturnTypesLength)
1706
+ const functionReturnNum = encodeU256Const(BigInt(functionReturnTypesLength))
1707
+
1708
+ const contractTemplateVar = '{0}' // always the 1st argument
1709
+ const externalCallInstr = encodeInstr(CallExternal(methodIndex))
1710
+ const numberOfInstrs = encodeI32(
1711
+ callerInstrs.length +
1712
+ approveAlphInstrs.length +
1713
+ approveTokensInstrs.length +
1714
+ templateVarStoreLocalInstrs.length +
1715
+ templateVarLoadLocalInstrs.length +
1716
+ functionReturnTypesLength +
1717
+ 4 // functionArgsNum, functionReturnNum, contractTemplate, externalCallInstr
1718
+ )
1719
+
1720
+ return (
1721
+ numberOfMethods +
1722
+ isPublic +
1723
+ modifier +
1724
+ argsLength +
1725
+ localsLength +
1726
+ returnsLength +
1727
+ numberOfInstrs +
1728
+ callerInstrs.join('') +
1729
+ approveAlphInstrs.join('') +
1730
+ approveTokensInstrs.join('') +
1731
+ templateVarStoreLocalInstrs.join('') +
1732
+ templateVarLoadLocalInstrs.join('') +
1733
+ functionArgsNum +
1734
+ functionReturnNum +
1735
+ contractTemplateVar +
1736
+ externalCallInstr +
1737
+ functionReturnPopInstrs
1738
+ )
1739
+ }
1740
+
1741
+ function getApproveAlphInstrs(attoAlphAmount?: Number256): string[] {
1742
+ const approveAlphInstrs: string[] = []
1743
+ if (attoAlphAmount) {
1744
+ const approvedAttoAlphAmount = encodeU256Const(BigInt(attoAlphAmount))
1745
+ approveAlphInstrs.push(approvedAttoAlphAmount)
1746
+ approveAlphInstrs.push(encodeInstr(ApproveAlph))
1747
+ }
1748
+
1749
+ return approveAlphInstrs
1750
+ }
1751
+
1752
+ function getApproveTokensInstrs(tokens?: Token[]): string[] {
1753
+ const approveTokensInstrs: string[] = []
1754
+ if (tokens) {
1755
+ tokens.forEach((token) => {
1756
+ const tokenIdBin = hexToBinUnsafe(token.id)
1757
+ approveTokensInstrs.push(
1758
+ encodeInstr(
1759
+ ByteConst({
1760
+ length: compactSignedIntCodec.fromI32(tokenIdBin.length),
1761
+ value: tokenIdBin
1762
+ })
1763
+ )
1764
+ )
1765
+ approveTokensInstrs.push(encodeU256Const(BigInt(token.amount)))
1766
+ approveTokensInstrs.push(encodeInstr(ApproveToken))
1767
+ })
1768
+ }
1769
+
1770
+ return approveTokensInstrs
1771
+ }
1772
+
1773
+ function getCallAddressInstrs(approveAssetsNum: number): string[] {
1774
+ const callerInstrs: string[] = []
1775
+ if (approveAssetsNum > 0) {
1776
+ callerInstrs.push(encodeInstr(CallerAddress))
1777
+
1778
+ const dup = encodeInstr(Dup)
1779
+ if (approveAssetsNum > 1) {
1780
+ callerInstrs.push(...new Array(approveAssetsNum - 1).fill(dup))
1781
+ }
1782
+ }
1783
+
1784
+ return callerInstrs
1785
+ }
1786
+
1787
+ function getTemplateVarStoreLocalInstrs(functionSig: FunctionSig, structs: Struct[]): [string[], number] {
1788
+ let templateVarIndex = 1 // Start from 1 since first one is always the contract id
1789
+ let localsLength = 0
1790
+ const templateVarStoreInstrs: string[] = []
1791
+ functionSig.paramTypes.forEach((paramType) => {
1792
+ const fieldsLength = ralph.typeLength(paramType, structs)
1793
+ if (fieldsLength > 1) {
1794
+ for (let i = 0; i < fieldsLength; i++) {
1795
+ templateVarStoreInstrs.push(`{${templateVarIndex + i}}`)
1796
+ }
1797
+ for (let i = 0; i < fieldsLength; i++) {
1798
+ templateVarStoreInstrs.push(encodeStoreLocalInstr(localsLength + (fieldsLength - i - 1)))
1799
+ }
1800
+
1801
+ localsLength = localsLength + fieldsLength
1802
+ }
1803
+
1804
+ templateVarIndex = templateVarIndex + fieldsLength
1805
+ })
1806
+
1807
+ return [templateVarStoreInstrs, templateVarIndex]
1808
+ }
1809
+
1810
+ function getTemplateVarLoadLocalInstrs(functionSig: FunctionSig, structs: Struct[]): string[] {
1811
+ let templateVarIndex = 1
1812
+ let loadIndex = 0
1813
+ const templateVarLoadInstrs: string[] = []
1814
+ functionSig.paramTypes.forEach((paramType) => {
1815
+ const fieldsLength = ralph.typeLength(paramType, structs)
1816
+
1817
+ if (fieldsLength === 1) {
1818
+ templateVarLoadInstrs.push(`{${templateVarIndex}}`)
1819
+ }
1820
+
1821
+ if (fieldsLength > 1) {
1822
+ for (let i = 0; i < fieldsLength; i++) {
1823
+ templateVarLoadInstrs.push(encodeLoadLocalInstr(loadIndex + i))
1824
+ }
1825
+
1826
+ loadIndex = loadIndex + fieldsLength
1827
+ }
1828
+
1829
+ templateVarIndex = templateVarIndex + fieldsLength
1830
+ })
1831
+
1832
+ return templateVarLoadInstrs
1833
+ }
1834
+
1835
+ function encodeStoreLocalInstr(index: number): string {
1836
+ if (index < 0 || index > 0xff) {
1837
+ throw new Error(`StoreLocal index ${index} must be between 0 and 255 inclusive`)
1838
+ }
1839
+ return encodeInstr(StoreLocal(index))
1840
+ }
1841
+
1842
+ function encodeLoadLocalInstr(index: number): string {
1843
+ if (index < 0 || index > 0xff) {
1844
+ throw new Error(`LoadLocal index ${index} must be between 0 and 255 inclusive`)
1845
+ }
1846
+
1847
+ return encodeInstr(LoadLocal(index))
1848
+ }
1849
+
1850
+ function encodeI32(value: number): string {
1851
+ return binToHex(compactSignedIntCodec.encodeI32(value))
1852
+ }
1853
+
1854
+ function encodeU256Const(value: bigint): string {
1855
+ if (value < 0) {
1856
+ throw new Error(`value ${value} must be non-negative`)
1857
+ }
1858
+
1859
+ if (value < 6) {
1860
+ return (BigInt(0x0c) + value).toString(16).padStart(2, '0')
1861
+ } else {
1862
+ return encodeInstr(U256Const(compactUnsignedIntCodec.fromU256(BigInt(value))))
1863
+ }
1864
+ }
1865
+
1866
+ function encodeInstr(instr: Instr): string {
1867
+ return binToHex(instrCodec.encode(instr))
1868
+ }
1869
+
1870
+ function toFieldsSig(contractName: string, functionSig: FunctionSig): FieldsSig {
1871
+ return {
1872
+ names: ['__contract__'].concat(functionSig.paramNames),
1873
+ types: [contractName].concat(functionSig.paramTypes),
1874
+ isMutable: [false].concat(functionSig.paramIsMutable)
1875
+ }
1876
+ }
1877
+
1609
1878
  export async function multicallMethods<I extends ContractInstance, F extends Fields>(
1610
1879
  contract: ContractFactory<I, F>,
1611
1880
  instance: ContractInstance,
@@ -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 { Val, decodeArrayType, toApiAddress, toApiBoolean, toApiByteVec, toApiNumber256 } from '../api'
19
+ import { Val, decodeArrayType, toApiAddress, toApiBoolean, toApiByteVec, toApiNumber256, PrimitiveTypes } from '../api'
20
20
  import { HexString, binToHex, bs58, concatBytes, hexToBinUnsafe, isHexString } from '../utils'
21
21
  import { Fields, FieldsSig, Struct } from './contract'
22
22
  import { compactSignedIntCodec, compactUnsignedIntCodec } from '../codec'
@@ -411,6 +411,24 @@ export function primitiveToByteVec(value: Val, type: string): Uint8Array {
411
411
  }
412
412
  }
413
413
 
414
+ export function typeLength(typ: string, structs: Struct[]): number {
415
+ if (PrimitiveTypes.includes(typ)) {
416
+ return 1
417
+ }
418
+
419
+ if (typ.startsWith('[')) {
420
+ const [baseType, size] = decodeArrayType(typ)
421
+ return size * typeLength(baseType, structs)
422
+ }
423
+
424
+ const struct = structs.find((s) => s.name === typ)
425
+ if (struct !== undefined) {
426
+ return struct.fieldTypes.reduce((acc, fieldType) => acc + typeLength(fieldType, structs), 0)
427
+ }
428
+
429
+ return 1
430
+ }
431
+
414
432
  export function flattenFields(
415
433
  fields: Fields,
416
434
  names: string[],