@alephium/web3 1.3.0 → 1.5.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.
Files changed (107) hide show
  1. package/dist/alephium-web3.min.js +1 -1
  2. package/dist/alephium-web3.min.js.map +1 -1
  3. package/dist/src/address/address.js +11 -5
  4. package/dist/src/api/api-alephium.d.ts +2 -4
  5. package/dist/src/api/api-alephium.js +1 -1
  6. package/dist/src/codec/array-codec.d.ts +4 -12
  7. package/dist/src/codec/array-codec.js +15 -28
  8. package/dist/src/codec/asset-output-codec.d.ts +6 -11
  9. package/dist/src/codec/asset-output-codec.js +32 -71
  10. package/dist/src/codec/bigint-codec.d.ts +2 -1
  11. package/dist/src/codec/bigint-codec.js +14 -2
  12. package/dist/src/codec/bytestring-codec.d.ts +6 -11
  13. package/dist/src/codec/bytestring-codec.js +9 -23
  14. package/dist/src/codec/codec.d.ts +54 -5
  15. package/dist/src/codec/codec.js +112 -14
  16. package/dist/src/codec/compact-int-codec.d.ts +65 -44
  17. package/dist/src/codec/compact-int-codec.js +222 -204
  18. package/dist/src/codec/contract-codec.d.ts +5 -8
  19. package/dist/src/codec/contract-codec.js +15 -29
  20. package/dist/src/codec/contract-output-codec.d.ts +4 -10
  21. package/dist/src/codec/contract-output-codec.js +20 -40
  22. package/dist/src/codec/contract-output-ref-codec.d.ts +2 -8
  23. package/dist/src/codec/contract-output-ref-codec.js +7 -17
  24. package/dist/src/codec/either-codec.d.ts +8 -15
  25. package/dist/src/codec/either-codec.js +5 -46
  26. package/dist/src/codec/index.d.ts +4 -3
  27. package/dist/src/codec/index.js +7 -4
  28. package/dist/src/codec/input-codec.d.ts +4 -10
  29. package/dist/src/codec/input-codec.js +11 -46
  30. package/dist/src/codec/instr-codec.d.ts +633 -40
  31. package/dist/src/codec/instr-codec.js +1040 -434
  32. package/dist/src/codec/int-as-4bytes-codec.d.ts +7 -0
  33. package/dist/src/codec/{signed-int-codec.js → int-as-4bytes-codec.js} +6 -12
  34. package/dist/src/codec/lockup-script-codec.d.ts +23 -26
  35. package/dist/src/codec/lockup-script-codec.js +12 -58
  36. package/dist/src/codec/method-codec.d.ts +6 -18
  37. package/dist/src/codec/method-codec.js +20 -48
  38. package/dist/src/codec/option-codec.d.ts +8 -13
  39. package/dist/src/codec/option-codec.js +14 -32
  40. package/dist/src/codec/output-codec.d.ts +2 -2
  41. package/dist/src/codec/output-codec.js +1 -1
  42. package/dist/src/codec/reader.d.ts +8 -0
  43. package/dist/src/codec/reader.js +48 -0
  44. package/dist/src/codec/script-codec.d.ts +6 -14
  45. package/dist/src/codec/script-codec.js +6 -22
  46. package/dist/src/codec/signature-codec.d.ts +4 -12
  47. package/dist/src/codec/signature-codec.js +3 -15
  48. package/dist/src/codec/timestamp-codec.d.ts +8 -0
  49. package/dist/src/codec/timestamp-codec.js +39 -0
  50. package/dist/src/codec/token-codec.d.ts +3 -10
  51. package/dist/src/codec/token-codec.js +6 -24
  52. package/dist/src/codec/transaction-codec.d.ts +6 -11
  53. package/dist/src/codec/transaction-codec.js +24 -49
  54. package/dist/src/codec/unlock-script-codec.d.ts +25 -36
  55. package/dist/src/codec/unlock-script-codec.js +26 -147
  56. package/dist/src/codec/unsigned-tx-codec.d.ts +8 -14
  57. package/dist/src/codec/unsigned-tx-codec.js +24 -66
  58. package/dist/src/codec/val.d.ts +27 -0
  59. package/dist/src/codec/val.js +33 -0
  60. package/dist/src/contract/contract.d.ts +1 -0
  61. package/dist/src/contract/contract.js +20 -13
  62. package/dist/src/contract/index.d.ts +1 -0
  63. package/dist/src/contract/index.js +1 -0
  64. package/dist/src/contract/ralph.d.ts +0 -4
  65. package/dist/src/contract/ralph.js +50 -179
  66. package/dist/src/contract/script-simulator.d.ts +27 -0
  67. package/dist/src/contract/script-simulator.js +757 -0
  68. package/dist/src/exchange/exchange.js +1 -1
  69. package/package.json +3 -4
  70. package/src/address/address.ts +15 -9
  71. package/src/api/api-alephium.ts +2 -4
  72. package/src/codec/array-codec.ts +16 -34
  73. package/src/codec/asset-output-codec.ts +38 -83
  74. package/src/codec/bigint-codec.ts +16 -2
  75. package/src/codec/bytestring-codec.ts +10 -28
  76. package/src/codec/codec.ts +121 -15
  77. package/src/codec/compact-int-codec.ts +230 -207
  78. package/src/codec/contract-codec.ts +20 -33
  79. package/src/codec/contract-output-codec.ts +22 -48
  80. package/src/codec/contract-output-ref-codec.ts +6 -17
  81. package/src/codec/either-codec.ts +4 -53
  82. package/src/codec/index.ts +4 -3
  83. package/src/codec/input-codec.ts +14 -36
  84. package/src/codec/instr-codec.ts +1229 -455
  85. package/src/codec/{signed-int-codec.ts → int-as-4bytes-codec.ts} +6 -10
  86. package/src/codec/lockup-script-codec.ts +28 -76
  87. package/src/codec/method-codec.ts +23 -61
  88. package/src/codec/option-codec.ts +13 -36
  89. package/src/codec/output-codec.ts +2 -2
  90. package/src/codec/reader.ts +56 -0
  91. package/src/codec/script-codec.ts +9 -31
  92. package/src/codec/signature-codec.ts +3 -18
  93. package/src/codec/timestamp-codec.ts +42 -0
  94. package/src/codec/token-codec.ts +7 -26
  95. package/src/codec/transaction-codec.ts +29 -58
  96. package/src/codec/unlock-script-codec.ts +44 -171
  97. package/src/codec/unsigned-tx-codec.ts +34 -63
  98. package/src/codec/val.ts +40 -0
  99. package/src/contract/contract.ts +24 -20
  100. package/src/contract/index.ts +1 -0
  101. package/src/contract/ralph.ts +76 -172
  102. package/src/contract/script-simulator.ts +838 -0
  103. package/src/exchange/exchange.ts +1 -1
  104. package/dist/src/codec/long-codec.d.ts +0 -8
  105. package/dist/src/codec/long-codec.js +0 -55
  106. package/dist/src/codec/signed-int-codec.d.ts +0 -8
  107. package/src/codec/long-codec.ts +0 -58
@@ -106,7 +106,7 @@ function getAddressFromUnlockScript(unlockScript) {
106
106
  if (unlockScriptType === UnlockScriptType.P2SH) {
107
107
  let p2sh;
108
108
  try {
109
- p2sh = unlock_script_codec_1.unlockScriptCodec.decode(decoded).script;
109
+ p2sh = unlock_script_codec_1.unlockScriptCodec.decode(decoded).value;
110
110
  }
111
111
  catch (_) {
112
112
  throw new Error(`Invalid p2sh unlock script: ${unlockScript}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alephium/web3",
3
- "version": "1.3.0",
3
+ "version": "1.5.0",
4
4
  "description": "A JS/TS library to interact with the Alephium platform",
5
5
  "license": "GPL",
6
6
  "main": "dist/src/index.js",
@@ -33,7 +33,7 @@
33
33
  },
34
34
  "author": "Alephium dev <dev@alephium.org>",
35
35
  "config": {
36
- "alephium_version": "3.4.0",
36
+ "alephium_version": "3.5.2",
37
37
  "explorer_backend_version": "2.0.0"
38
38
  },
39
39
  "type": "commonjs",
@@ -48,8 +48,7 @@
48
48
  "elliptic": "6.5.4",
49
49
  "eventemitter3": "^4.0.7",
50
50
  "path-browserify": "^1.0.1",
51
- "stream-browserify": "^3.0.0",
52
- "binary-parser": "2.2.1"
51
+ "stream-browserify": "^3.0.0"
53
52
  },
54
53
  "devDependencies": {
55
54
  "@babel/eslint-parser": "^7.21.3",
@@ -22,10 +22,10 @@ import { TOTAL_NUMBER_OF_GROUPS } from '../constants'
22
22
  import blake from 'blakejs'
23
23
  import bs58 from '../utils/bs58'
24
24
  import djb2 from '../utils/djb2'
25
- import { binToHex, concatBytes, hexToBinUnsafe } from '../utils'
25
+ import { binToHex, concatBytes, hexToBinUnsafe, isHexString } from '../utils'
26
26
  import { KeyType } from '../signer'
27
- import { MultiSig, lockupScriptCodec } from '../codec/lockup-script-codec'
28
- import { compactSignedIntCodec } from '../codec'
27
+ import { P2MPKH, lockupScriptCodec } from '../codec/lockup-script-codec'
28
+ import { i32Codec } from '../codec'
29
29
 
30
30
  const ec = new EC('secp256k1')
31
31
  const PublicKeyHashSize = 32
@@ -61,19 +61,19 @@ function decodeAndValidateAddress(address: string): Uint8Array {
61
61
  if (decoded.length === 0) throw new Error('Address is empty')
62
62
  const addressType = decoded[0]
63
63
  if (addressType === AddressType.P2MPKH) {
64
- let multisig: MultiSig
64
+ let multisig: P2MPKH
65
65
  try {
66
- multisig = lockupScriptCodec.decode(decoded).script as MultiSig
66
+ multisig = lockupScriptCodec.decode(decoded).value as P2MPKH
67
67
  } catch (_) {
68
68
  throw new Error(`Invalid multisig address: ${address}`)
69
69
  }
70
- const n = multisig.publicKeyHashes.value.length
71
- const m = compactSignedIntCodec.toI32(multisig.m)
70
+ const n = multisig.publicKeyHashes.length
71
+ const m = multisig.m
72
72
  if (n < m || m <= 0) {
73
73
  throw new Error(`Invalid multisig address, n: ${n}, m: ${m}`)
74
74
  }
75
- const encodedNSize = compactSignedIntCodec.encodeI32(n).length
76
- const encodedMSize = multisig.m.rest.length + 1
75
+ const encodedNSize = i32Codec.encode(n).length
76
+ const encodedMSize = i32Codec.encode(m).length
77
77
  const size = encodedNSize + PublicKeyHashSize * n + encodedMSize + 1 // 1 for the P2MPKH prefix
78
78
  if (decoded.length === size) return decoded
79
79
  } else if (addressType === AddressType.P2PKH || addressType === AddressType.P2SH || addressType === AddressType.P2C) {
@@ -211,6 +211,12 @@ export function subContractId(parentContractId: string, pathInHex: string, group
211
211
  if (group < 0 || group >= TOTAL_NUMBER_OF_GROUPS) {
212
212
  throw new Error(`Invalid group ${group}`)
213
213
  }
214
+ if (!isHexString(parentContractId)) {
215
+ throw new Error(`Invalid parent contract ID: ${parentContractId}, expected hex string`)
216
+ }
217
+ if (!isHexString(pathInHex)) {
218
+ throw new Error(`Invalid path: ${pathInHex}, expected hex string`)
219
+ }
214
220
  const data = concatBytes([hexToBinUnsafe(parentContractId), hexToBinUnsafe(pathInHex)])
215
221
  const bytes = new Uint8Array([
216
222
  ...blake.blake2b(blake.blake2b(data, undefined, 32), undefined, 32).slice(0, -1),
@@ -21,7 +21,6 @@ export interface AddressBalance {
21
21
  lockedBalance: string
22
22
  /** @format x.x ALPH */
23
23
  lockedBalanceHint: string
24
- warning?: string
25
24
  }
26
25
 
27
26
  /** AddressInfo */
@@ -93,7 +92,6 @@ export interface Balance {
93
92
  lockedTokenBalances?: Token[]
94
93
  /** @format int32 */
95
94
  utxoNum: number
96
- warning?: string
97
95
  }
98
96
 
99
97
  /** Balances */
@@ -527,6 +525,7 @@ export interface CompilerOptions {
527
525
  ignoreUnusedPrivateFunctionsWarnings?: boolean
528
526
  ignoreUpdateFieldsCheckWarnings?: boolean
529
527
  ignoreCheckExternalCallerWarnings?: boolean
528
+ ignoreUnusedFunctionReturnWarnings?: boolean
530
529
  }
531
530
 
532
531
  /** Confirmed */
@@ -1146,7 +1145,6 @@ export interface UTXO {
1146
1145
  /** UTXOs */
1147
1146
  export interface UTXOs {
1148
1147
  utxos: UTXO[]
1149
- warning?: string
1150
1148
  }
1151
1149
 
1152
1150
  /** Unauthorized */
@@ -1495,7 +1493,7 @@ export class HttpClient<SecurityDataType = unknown> {
1495
1493
 
1496
1494
  /**
1497
1495
  * @title Alephium API
1498
- * @version 3.4.0
1496
+ * @version 3.5.2
1499
1497
  * @baseUrl ../
1500
1498
  */
1501
1499
  export class Api<SecurityDataType extends unknown> extends HttpClient<SecurityDataType> {
@@ -15,48 +15,30 @@ GNU Lesser General Public License for more details.
15
15
  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
- import { Parser } from 'binary-parser'
19
- import { compactSignedIntCodec, DecodedCompactInt } from './compact-int-codec'
18
+ import { i32Codec } from './compact-int-codec'
20
19
  import { Codec } from './codec'
20
+ import { Reader } from './reader'
21
+ import { concatBytes } from '../utils'
21
22
 
22
- export interface DecodedArray<T> {
23
- length: DecodedCompactInt
24
- value: T[]
25
- }
26
-
27
- export class ArrayCodec<T> implements Codec<T[]> {
28
- constructor(private childCodec: Codec<T>, public parser = ArrayCodec.arrayParser(childCodec.parser)) {}
23
+ export class ArrayCodec<T> extends Codec<T[]> {
24
+ constructor(private childCodec: Codec<T>) {
25
+ super()
26
+ }
29
27
 
30
28
  encode(input: T[]): Uint8Array {
31
- const result = [...compactSignedIntCodec.encodeI256(BigInt(input.length))]
29
+ const bytes: Uint8Array[] = [i32Codec.encode(input.length)]
32
30
  for (const element of input) {
33
- result.push(...this.childCodec.encode(element))
31
+ bytes.push(this.childCodec.encode(element))
34
32
  }
35
- return new Uint8Array(result)
36
- }
37
-
38
- decode(input: Uint8Array): T[] {
39
- const result = this.parser.parse(input)
40
- return result.value.map((v) => this.childCodec.decode(v.value))
41
- }
42
-
43
- static arrayParser(parser: Parser) {
44
- return new Parser()
45
- .nest('length', {
46
- type: compactSignedIntCodec.parser
47
- })
48
- .array('value', {
49
- length: function (ctx) {
50
- return compactSignedIntCodec.toI32(this['length']! as any as DecodedCompactInt)
51
- },
52
- type: parser
53
- })
33
+ return concatBytes(bytes)
54
34
  }
55
35
 
56
- fromArray(inputs: T[]): DecodedArray<T> {
57
- return {
58
- length: compactSignedIntCodec.fromI32(inputs.length),
59
- value: inputs
36
+ _decode(input: Reader): T[] {
37
+ const length = i32Codec._decode(input)
38
+ const array: T[] = []
39
+ for (let index = 0; index < length; index += 1) {
40
+ array.push(this.childCodec._decode(input))
60
41
  }
42
+ return array
61
43
  }
62
44
  }
@@ -15,89 +15,54 @@ GNU Lesser General Public License for more details.
15
15
  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
- import { Parser } from 'binary-parser'
19
- import { ArrayCodec, DecodedArray } from './array-codec'
20
- import { DecodedCompactInt, compactSignedIntCodec, compactUnsignedIntCodec } from './compact-int-codec'
21
- import { signedIntCodec } from './signed-int-codec'
22
- import { longCodec } from './long-codec'
18
+ import { ArrayCodec } from './array-codec'
19
+ import { u256Codec } from './compact-int-codec'
20
+ import { intAs4BytesCodec } from './int-as-4bytes-codec'
21
+ import { timestampCodec } from './timestamp-codec'
23
22
  import { ByteString, byteStringCodec } from './bytestring-codec'
24
- import { LockupScript, MultiSig, P2SH, lockupScriptCodec } from './lockup-script-codec'
23
+ import { LockupScript, P2MPKH, P2PKH, P2SH, lockupScriptCodec } from './lockup-script-codec'
25
24
  import { FixedAssetOutput } from '../api/api-alephium'
26
25
  import { blakeHash, createHint } from './hash'
27
26
  import { bs58, binToHex, hexToBinUnsafe, concatBytes } from '../utils'
28
- import { Codec } from './codec'
29
- import { PublicKeyHash } from './lockup-script-codec'
27
+ import { ObjectCodec } from './codec'
30
28
  import { Token, tokensCodec } from './token-codec'
31
29
 
32
30
  export interface AssetOutput {
33
- amount: DecodedCompactInt
31
+ amount: bigint
34
32
  lockupScript: LockupScript
35
- lockTime: Uint8Array
36
- tokens: DecodedArray<Token>
33
+ lockTime: bigint
34
+ tokens: Token[]
37
35
  additionalData: ByteString
38
36
  }
39
37
 
40
- export class AssetOutputCodec implements Codec<AssetOutput> {
41
- parser = Parser.start()
42
- .nest('amount', {
43
- type: compactSignedIntCodec.parser
44
- })
45
- .nest('lockupScript', {
46
- type: lockupScriptCodec.parser
47
- })
48
- .buffer('lockTime', {
49
- length: 8
50
- })
51
- .nest('tokens', {
52
- type: tokensCodec.parser
53
- })
54
- .nest('additionalData', {
55
- type: byteStringCodec.parser
56
- })
57
-
58
- encode(input: AssetOutput): Uint8Array {
59
- const amount = compactUnsignedIntCodec.encode(input.amount)
60
- const lockupScript = lockupScriptCodec.encode(input.lockupScript)
61
- const tokens = tokensCodec.encode(input.tokens.value)
62
- const additionalData = byteStringCodec.encode(input.additionalData)
63
-
64
- return concatBytes([amount, lockupScript, input.lockTime, tokens, additionalData])
65
- }
66
-
67
- decode(input: Uint8Array): AssetOutput {
68
- return this.parser.parse(input)
69
- }
70
-
38
+ export class AssetOutputCodec extends ObjectCodec<AssetOutput> {
71
39
  static toFixedAssetOutputs(txIdBytes: Uint8Array, outputs: AssetOutput[]): FixedAssetOutput[] {
72
40
  return outputs.map((output, index) => AssetOutputCodec.toFixedAssetOutput(txIdBytes, output, index))
73
41
  }
74
42
 
75
43
  static toFixedAssetOutput(txIdBytes: Uint8Array, output: AssetOutput, index: number): FixedAssetOutput {
76
- const attoAlphAmount = compactUnsignedIntCodec.toU256(output.amount).toString()
77
- const lockTime = Number(longCodec.decode(output.lockTime))
78
- const tokens = output.tokens.value.map((token) => {
44
+ const attoAlphAmount = output.amount.toString()
45
+ const lockTime = Number(output.lockTime)
46
+ const tokens = output.tokens.map((token) => {
79
47
  return {
80
48
  id: binToHex(token.tokenId),
81
- amount: compactUnsignedIntCodec.toU256(token.amount).toString()
49
+ amount: token.amount.toString()
82
50
  }
83
51
  })
84
- const message = binToHex(output.additionalData.value)
85
- const scriptType = output.lockupScript.scriptType
86
- const key = binToHex(blakeHash(concatBytes([txIdBytes, signedIntCodec.encode(index)])))
87
- const outputLockupScript = output.lockupScript.script
52
+ const message = binToHex(output.additionalData)
53
+ const scriptType = output.lockupScript.kind
54
+ const key = binToHex(blakeHash(concatBytes([txIdBytes, intAs4BytesCodec.encode(index)])))
55
+ const outputLockupScript = output.lockupScript.value
88
56
  const address = bs58.encode(lockupScriptCodec.encode(output.lockupScript))
89
57
 
90
58
  let hint: number | undefined = undefined
91
- if (scriptType === 0) {
92
- // P2PKH
93
- hint = createHint((outputLockupScript as PublicKeyHash).publicKeyHash)
94
- } else if (scriptType === 1) {
95
- // P2MPKH
96
- hint = createHint((outputLockupScript as MultiSig).publicKeyHashes.value[0].publicKeyHash)
97
- } else if (scriptType === 2) {
98
- // P2SH
99
- hint = createHint((outputLockupScript as P2SH).scriptHash)
100
- } else if (scriptType === 3) {
59
+ if (scriptType === 'P2PKH') {
60
+ hint = createHint(outputLockupScript as P2PKH)
61
+ } else if (scriptType === 'P2MPKH') {
62
+ hint = createHint((outputLockupScript as P2MPKH).publicKeyHashes[0])
63
+ } else if (scriptType === 'P2SH') {
64
+ hint = createHint(outputLockupScript as P2SH)
65
+ } else if (scriptType === 'P2C') {
101
66
  throw new Error(`P2C script type not allowed for asset output`)
102
67
  } else {
103
68
  throw new Error(`Unexpected output script type: ${scriptType}`)
@@ -113,35 +78,25 @@ export class AssetOutputCodec implements Codec<AssetOutput> {
113
78
  }
114
79
 
115
80
  static fromFixedAssetOutput(fixedOutput: FixedAssetOutput): AssetOutput {
116
- const amount: DecodedCompactInt = compactUnsignedIntCodec.fromU256(BigInt(fixedOutput.attoAlphAmount))
117
-
118
- const lockTime = longCodec.encode(BigInt(fixedOutput.lockTime))
81
+ const amount = BigInt(fixedOutput.attoAlphAmount)
82
+ const lockTime = BigInt(fixedOutput.lockTime)
119
83
  const lockupScript: LockupScript = lockupScriptCodec.decode(bs58.decode(fixedOutput.address))
120
- const tokensValue = fixedOutput.tokens.map((token) => {
84
+ const tokens = fixedOutput.tokens.map((token) => {
121
85
  return {
122
86
  tokenId: hexToBinUnsafe(token.id),
123
- amount: compactUnsignedIntCodec.fromU256(BigInt(token.amount))
87
+ amount: BigInt(token.amount)
124
88
  }
125
89
  })
126
- const tokens: DecodedArray<Token> = {
127
- length: compactSignedIntCodec.fromI32(tokensValue.length),
128
- value: tokensValue
129
- }
130
- const additionalDataValue = hexToBinUnsafe(fixedOutput.message)
131
- const additionalData: ByteString = {
132
- length: compactSignedIntCodec.fromI32(additionalDataValue.length),
133
- value: additionalDataValue
134
- }
135
-
136
- return {
137
- amount,
138
- lockupScript,
139
- lockTime,
140
- tokens,
141
- additionalData
142
- }
90
+ const additionalData = hexToBinUnsafe(fixedOutput.message)
91
+ return { amount, lockupScript, lockTime, tokens, additionalData }
143
92
  }
144
93
  }
145
94
 
146
- export const assetOutputCodec = new AssetOutputCodec()
95
+ export const assetOutputCodec = new AssetOutputCodec({
96
+ amount: u256Codec,
97
+ lockupScript: lockupScriptCodec,
98
+ lockTime: timestampCodec,
99
+ tokens: tokensCodec,
100
+ additionalData: byteStringCodec
101
+ })
147
102
  export const assetOutputsCodec = new ArrayCodec(assetOutputCodec)
@@ -64,14 +64,28 @@ export class BigIntCodec {
64
64
  return new Uint8Array(bytes.reverse())
65
65
  }
66
66
 
67
- static decode(encoded: Uint8Array, signed: boolean): bigint {
67
+ static decodeUnsigned(encoded: Uint8Array): bigint {
68
+ // Special case for zero.
69
+ if (encoded.length === 1 && encoded[0] === 0) {
70
+ return 0n
71
+ }
72
+
73
+ // Convert the byte array to a bigint
74
+ let value = 0n
75
+ for (const byte of encoded) {
76
+ value = (value << 8n) | BigInt(byte)
77
+ }
78
+ return value
79
+ }
80
+
81
+ static decodeSigned(encoded: Uint8Array): bigint {
68
82
  // Special case for zero.
69
83
  if (encoded.length === 1 && encoded[0] === 0) {
70
84
  return 0n
71
85
  }
72
86
 
73
87
  // Determine if the number is negative by checking the most significant byte (MSB)
74
- const isNegative = signed ? encoded[0] === 0xff : signed
88
+ const isNegative = (encoded[0] & 0x80) !== 0
75
89
 
76
90
  // Convert the byte array to a bigint
77
91
  let value = 0n
@@ -15,42 +15,24 @@ GNU Lesser General Public License for more details.
15
15
  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
- import { Parser } from 'binary-parser'
19
- import { DecodedCompactInt, compactSignedIntCodec } from './compact-int-codec'
18
+ import { i32Codec } from './compact-int-codec'
20
19
  import { Codec } from './codec'
21
20
  import { concatBytes } from '../utils'
21
+ import { Reader } from './reader'
22
+ import { ArrayCodec } from './array-codec'
22
23
 
23
- export interface ByteString {
24
- length: DecodedCompactInt
25
- value: Uint8Array
26
- }
27
-
28
- export class ByteStringCodec implements Codec<ByteString> {
29
- parser = new Parser()
30
- .nest('length', {
31
- type: compactSignedIntCodec.parser
32
- })
33
- .buffer('value', {
34
- length: function (ctx) {
35
- return compactSignedIntCodec.toI32(this['length']! as any as DecodedCompactInt)
36
- }
37
- })
24
+ export type ByteString = Uint8Array
38
25
 
26
+ export class ByteStringCodec extends Codec<ByteString> {
39
27
  encode(input: ByteString): Uint8Array {
40
- return concatBytes([compactSignedIntCodec.encode(input.length), input.value])
41
- }
42
-
43
- decode(input: Uint8Array): ByteString {
44
- return this.parser.parse(input)
45
- }
46
-
47
- encodeBytes(input: Uint8Array): Uint8Array {
48
- return concatBytes([compactSignedIntCodec.encodeI32(input.length), input])
28
+ return concatBytes([i32Codec.encode(input.length), input])
49
29
  }
50
30
 
51
- decodeBytes(input: Uint8Array): Uint8Array {
52
- return this.decode(input).value
31
+ _decode(input: Reader): ByteString {
32
+ const length = i32Codec._decode(input)
33
+ return input.consumeBytes(length)
53
34
  }
54
35
  }
55
36
 
56
37
  export const byteStringCodec = new ByteStringCodec()
38
+ export const byteStringsCodec = new ArrayCodec(byteStringCodec)
@@ -15,12 +15,33 @@ GNU Lesser General Public License for more details.
15
15
  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
- import { Parser } from 'binary-parser'
19
18
 
20
- export interface Codec<T> {
21
- parser: Parser
22
- encode(input: T): Uint8Array
23
- decode(input: Uint8Array): T
19
+ import { concatBytes } from '../utils'
20
+ import { Reader } from './reader'
21
+
22
+ export abstract class Codec<T> {
23
+ abstract encode(input: T): Uint8Array
24
+ decode(input: Uint8Array): T {
25
+ const reader = new Reader(input)
26
+ return this._decode(reader)
27
+ }
28
+
29
+ abstract _decode(input: Reader): T
30
+
31
+ bimap<R>(from: (v: T) => R, to: (v: R) => T): Codec<R> {
32
+ return new (class extends Codec<R> {
33
+ constructor(private readonly codecT: Codec<T>) {
34
+ super()
35
+ }
36
+
37
+ encode(input: R): Uint8Array {
38
+ return this.codecT.encode(to(input))
39
+ }
40
+ _decode(input: Reader): R {
41
+ return from(this.codecT._decode(input))
42
+ }
43
+ })(this)
44
+ }
24
45
  }
25
46
 
26
47
  export function assert(value: boolean, message: string) {
@@ -29,15 +50,100 @@ export function assert(value: boolean, message: string) {
29
50
  }
30
51
  }
31
52
 
32
- export function fixedSizeBytes(name: string, length: number): Parser {
33
- return Parser.start().wrapped({
34
- length,
35
- type: Parser.start().buffer(name, { length }),
36
- wrapper: function (result) {
37
- if (result.length === length) {
38
- return result
39
- }
40
- throw new Error(`Too few bytes when parsing ${name}, expected ${length}, got ${result.length}`)
53
+ export class FixedSizeCodec extends Codec<Uint8Array> {
54
+ constructor(private readonly size: number) {
55
+ super()
56
+ }
57
+
58
+ encode(input: Uint8Array): Uint8Array {
59
+ assert(input.length === this.size, `Invalid length, expected ${this.size}, got ${input.length}`)
60
+ return input
61
+ }
62
+
63
+ _decode(input: Reader): Uint8Array {
64
+ return input.consumeBytes(this.size)
65
+ }
66
+ }
67
+
68
+ export class ObjectCodec<T> extends Codec<T> {
69
+ private keys: (keyof T)[]
70
+
71
+ constructor(private codecs: { [K in keyof T]: Codec<T[K]> }) {
72
+ super()
73
+ this.keys = Object.keys(codecs) as (keyof T)[]
74
+ }
75
+
76
+ encode(value: T): Uint8Array {
77
+ const bytes: Uint8Array[] = []
78
+ for (const key of this.keys) {
79
+ bytes.push(this.codecs[key].encode(value[key]))
80
+ }
81
+ return concatBytes(bytes)
82
+ }
83
+
84
+ _decode(input: Reader): T {
85
+ const result: T = {} as T
86
+ for (const key of this.keys) {
87
+ result[key] = this.codecs[key]._decode(input)
41
88
  }
42
- })
89
+ return result as T
90
+ }
43
91
  }
92
+
93
+ type ExtractKind<T> = T extends { kind: infer U extends string } ? U : never
94
+ type ExtractValue<T, K> = T extends { kind: K; value: infer V } ? V : never
95
+
96
+ export class EnumCodec<T extends { kind: ExtractKind<T>; value: ExtractValue<T, ExtractKind<T>> }> extends Codec<T> {
97
+ private kinds: ExtractKind<T>[]
98
+
99
+ constructor(private name: string, private codecs: { [K in ExtractKind<T>]: Codec<ExtractValue<T, K>> }) {
100
+ super()
101
+ this.kinds = Object.keys(codecs) as ExtractKind<T>[]
102
+ }
103
+
104
+ encode(value: T): Uint8Array {
105
+ const index = this.kinds.findIndex((t) => t === value.kind)
106
+ if (index === -1) {
107
+ throw new Error(`Invalid ${this.name} kind ${value.kind}, expected one of ${this.kinds}`)
108
+ }
109
+ const codec = this.codecs[value.kind]
110
+ return new Uint8Array([index, ...codec.encode(value.value)])
111
+ }
112
+
113
+ _decode(input: Reader): T {
114
+ const index = input.consumeByte()
115
+ if (index >= 0 && index < this.kinds.length) {
116
+ const kind = this.kinds[`${index}`]
117
+ const codec = this.codecs[kind as ExtractKind<T>]
118
+ return { kind, value: codec._decode(input) } as T
119
+ }
120
+ throw new Error(`Invalid encoded ${this.name} kind: ${index}`)
121
+ }
122
+ }
123
+
124
+ export const byte32Codec = new FixedSizeCodec(32)
125
+ export const byteCodec = new (class extends Codec<number> {
126
+ encode(input: number): Uint8Array {
127
+ assert(input >= 0 && input < 256, `Invalid byte: ${input}`)
128
+ return new Uint8Array([input])
129
+ }
130
+ _decode(input: Reader): number {
131
+ return input.consumeByte()
132
+ }
133
+ })()
134
+
135
+ export const boolCodec = new (class extends Codec<boolean> {
136
+ encode(input: boolean): Uint8Array {
137
+ return new Uint8Array([input ? 1 : 0])
138
+ }
139
+ _decode(input: Reader): boolean {
140
+ const byte = input.consumeByte()
141
+ if (byte === 1) {
142
+ return true
143
+ } else if (byte === 0) {
144
+ return false
145
+ } else {
146
+ throw new Error(`Invalid encoded bool value ${byte}, expected 0 or 1`)
147
+ }
148
+ }
149
+ })()