@alephium/web3 1.4.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 (106) 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 +5 -5
  4. package/dist/src/api/api-alephium.d.ts +1 -1
  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.js +6 -6
  61. package/dist/src/contract/index.d.ts +1 -0
  62. package/dist/src/contract/index.js +1 -0
  63. package/dist/src/contract/ralph.d.ts +0 -4
  64. package/dist/src/contract/ralph.js +50 -179
  65. package/dist/src/contract/script-simulator.d.ts +27 -0
  66. package/dist/src/contract/script-simulator.js +757 -0
  67. package/dist/src/exchange/exchange.js +1 -1
  68. package/package.json +3 -4
  69. package/src/address/address.ts +8 -8
  70. package/src/api/api-alephium.ts +1 -1
  71. package/src/codec/array-codec.ts +16 -34
  72. package/src/codec/asset-output-codec.ts +38 -83
  73. package/src/codec/bigint-codec.ts +16 -2
  74. package/src/codec/bytestring-codec.ts +10 -28
  75. package/src/codec/codec.ts +121 -15
  76. package/src/codec/compact-int-codec.ts +230 -207
  77. package/src/codec/contract-codec.ts +20 -33
  78. package/src/codec/contract-output-codec.ts +22 -48
  79. package/src/codec/contract-output-ref-codec.ts +6 -17
  80. package/src/codec/either-codec.ts +4 -53
  81. package/src/codec/index.ts +4 -3
  82. package/src/codec/input-codec.ts +14 -36
  83. package/src/codec/instr-codec.ts +1229 -455
  84. package/src/codec/{signed-int-codec.ts → int-as-4bytes-codec.ts} +6 -10
  85. package/src/codec/lockup-script-codec.ts +28 -76
  86. package/src/codec/method-codec.ts +23 -61
  87. package/src/codec/option-codec.ts +13 -36
  88. package/src/codec/output-codec.ts +2 -2
  89. package/src/codec/reader.ts +56 -0
  90. package/src/codec/script-codec.ts +9 -31
  91. package/src/codec/signature-codec.ts +3 -18
  92. package/src/codec/timestamp-codec.ts +42 -0
  93. package/src/codec/token-codec.ts +7 -26
  94. package/src/codec/transaction-codec.ts +29 -58
  95. package/src/codec/unlock-script-codec.ts +44 -171
  96. package/src/codec/unsigned-tx-codec.ts +34 -63
  97. package/src/codec/val.ts +40 -0
  98. package/src/contract/contract.ts +9 -13
  99. package/src/contract/index.ts +1 -0
  100. package/src/contract/ralph.ts +76 -172
  101. package/src/contract/script-simulator.ts +838 -0
  102. package/src/exchange/exchange.ts +1 -1
  103. package/dist/src/codec/long-codec.d.ts +0 -8
  104. package/dist/src/codec/long-codec.js +0 -55
  105. package/dist/src/codec/signed-int-codec.d.ts +0 -8
  106. 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.4.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.5.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",
@@ -24,8 +24,8 @@ import bs58 from '../utils/bs58'
24
24
  import djb2 from '../utils/djb2'
25
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) {
@@ -1493,7 +1493,7 @@ export class HttpClient<SecurityDataType = unknown> {
1493
1493
 
1494
1494
  /**
1495
1495
  * @title Alephium API
1496
- * @version 3.5.0
1496
+ * @version 3.5.2
1497
1497
  * @baseUrl ../
1498
1498
  */
1499
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
+ })()