@aeternity/aepp-sdk 10.0.0 → 11.0.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 (238) hide show
  1. package/README.md +15 -14
  2. package/dist/aepp-sdk.browser-script.js +1 -1
  3. package/dist/aepp-sdk.browser-script.js.LICENSE.txt +7 -27
  4. package/dist/aepp-sdk.browser-script.js.map +1 -1
  5. package/dist/aepp-sdk.browser.js +1 -1
  6. package/dist/aepp-sdk.browser.js.map +1 -1
  7. package/dist/aepp-sdk.js +1 -1
  8. package/dist/aepp-sdk.js.map +1 -1
  9. package/es/account/{base.js → base.mjs} +20 -10
  10. package/es/account/base.mjs.map +1 -0
  11. package/es/account/{memory.js → memory.mjs} +29 -14
  12. package/es/account/memory.mjs.map +1 -0
  13. package/es/account/{multiple.js → multiple.mjs} +10 -9
  14. package/es/account/multiple.mjs.map +1 -0
  15. package/es/ae/{aens.js → aens.mjs} +18 -22
  16. package/es/ae/aens.mjs.map +1 -0
  17. package/es/ae/{aepp.js → aepp.mjs} +9 -9
  18. package/es/ae/aepp.mjs.map +1 -0
  19. package/es/ae/contract.mjs +159 -0
  20. package/es/ae/contract.mjs.map +1 -0
  21. package/es/ae/{index.js → index.mjs} +8 -7
  22. package/es/ae/index.mjs.map +1 -0
  23. package/es/ae/{oracle.js → oracle.mjs} +33 -16
  24. package/es/ae/oracle.mjs.map +1 -0
  25. package/es/ae/{universal.js → universal.mjs} +10 -10
  26. package/es/ae/universal.mjs.map +1 -0
  27. package/es/ae/{wallet.js → wallet.mjs} +10 -10
  28. package/es/ae/wallet.mjs.map +1 -0
  29. package/es/aepp-wallet-communication/schema.d.ts +74 -0
  30. package/es/bignumber.d.ts +1 -7
  31. package/es/chain/{index.js → index.mjs} +20 -4
  32. package/es/chain/index.mjs.map +1 -0
  33. package/es/chain/{node.js → node.mjs} +121 -73
  34. package/es/chain/node.mjs.map +1 -0
  35. package/es/channel/{handlers.js → handlers.mjs} +18 -20
  36. package/es/channel/handlers.mjs.map +1 -0
  37. package/es/channel/{index.js → index.mjs} +7 -7
  38. package/es/channel/index.mjs.map +1 -0
  39. package/es/channel/{internal.js → internal.mjs} +49 -32
  40. package/es/channel/internal.mjs.map +1 -0
  41. package/es/contract/aci.mjs +477 -0
  42. package/es/contract/aci.mjs.map +1 -0
  43. package/es/contract/compiler.mjs +115 -0
  44. package/es/contract/compiler.mjs.map +1 -0
  45. package/es/contract/ga/helpers.mjs +30 -0
  46. package/es/contract/ga/helpers.mjs.map +1 -0
  47. package/es/contract/ga/{index.js → index.mjs} +36 -28
  48. package/es/contract/ga/index.mjs.map +1 -0
  49. package/es/errors.d.ts +284 -0
  50. package/es/index.mjs +57 -0
  51. package/es/index.mjs.map +1 -0
  52. package/es/node-pool/{index.js → index.mjs} +11 -10
  53. package/es/node-pool/index.mjs.map +1 -0
  54. package/es/{node.js → node.mjs} +9 -8
  55. package/es/node.mjs.map +1 -0
  56. package/es/tx/builder/field-types.mjs +74 -0
  57. package/es/tx/builder/field-types.mjs.map +1 -0
  58. package/es/tx/builder/{helpers.js → helpers.mjs} +114 -56
  59. package/es/tx/builder/helpers.mjs.map +1 -0
  60. package/es/tx/builder/{index.js → index.mjs} +79 -68
  61. package/es/tx/builder/index.mjs.map +1 -0
  62. package/es/tx/builder/{schema.js → schema.mjs} +11 -65
  63. package/es/tx/builder/schema.mjs.map +1 -0
  64. package/es/tx/{index.js → index.mjs} +5 -2
  65. package/es/tx/index.mjs.map +1 -0
  66. package/es/tx/{tx-object.js → tx-object.mjs} +22 -15
  67. package/es/tx/tx-object.mjs.map +1 -0
  68. package/es/tx/{tx.js → tx.mjs} +15 -14
  69. package/es/tx/tx.mjs.map +1 -0
  70. package/es/tx/{validator.js → validator.mjs} +72 -33
  71. package/es/tx/validator.mjs.map +1 -0
  72. package/es/typings/blakejs/index.d.mjs +2 -0
  73. package/es/typings/blakejs/{index.d.js.map → index.d.mjs.map} +1 -1
  74. package/es/utils/aepp-wallet-communication/connection/{browser-runtime.js → browser-runtime.mjs} +7 -6
  75. package/es/utils/aepp-wallet-communication/connection/browser-runtime.mjs.map +1 -0
  76. package/es/utils/aepp-wallet-communication/connection/{browser-window-message.js → browser-window-message.mjs} +10 -11
  77. package/es/utils/aepp-wallet-communication/connection/browser-window-message.mjs.map +1 -0
  78. package/es/utils/aepp-wallet-communication/connection/{index.js → index.mjs} +5 -2
  79. package/es/utils/aepp-wallet-communication/connection/index.mjs.map +1 -0
  80. package/es/utils/aepp-wallet-communication/{content-script-bridge.js → content-script-bridge.mjs} +6 -5
  81. package/es/utils/aepp-wallet-communication/content-script-bridge.mjs.map +1 -0
  82. package/es/utils/aepp-wallet-communication/{helpers.js → helpers.mjs} +3 -14
  83. package/es/utils/aepp-wallet-communication/helpers.mjs.map +1 -0
  84. package/es/utils/aepp-wallet-communication/rpc/{aepp-rpc.js → aepp-rpc.mjs} +113 -67
  85. package/es/utils/aepp-wallet-communication/rpc/aepp-rpc.mjs.map +1 -0
  86. package/es/utils/aepp-wallet-communication/rpc/{rpc-client.js → rpc-client.mjs} +14 -9
  87. package/es/utils/aepp-wallet-communication/rpc/rpc-client.mjs.map +1 -0
  88. package/es/utils/aepp-wallet-communication/rpc/{wallet-rpc.js → wallet-rpc.mjs} +71 -63
  89. package/es/utils/aepp-wallet-communication/rpc/wallet-rpc.mjs.map +1 -0
  90. package/es/utils/aepp-wallet-communication/schema.mjs +102 -0
  91. package/es/utils/aepp-wallet-communication/schema.mjs.map +1 -0
  92. package/es/utils/aepp-wallet-communication/{wallet-detector.js → wallet-detector.mjs} +6 -5
  93. package/es/utils/aepp-wallet-communication/wallet-detector.mjs.map +1 -0
  94. package/es/utils/{amount-formatter.js → amount-formatter.mjs} +14 -7
  95. package/es/utils/amount-formatter.mjs.map +1 -0
  96. package/es/utils/{async-init.js → async-init.mjs} +8 -11
  97. package/es/utils/async-init.mjs.map +1 -0
  98. package/es/utils/{bignumber.js → bignumber.mjs} +2 -9
  99. package/es/utils/bignumber.mjs.map +1 -0
  100. package/es/utils/{bytes.js → bytes.mjs} +16 -8
  101. package/es/utils/bytes.mjs.map +1 -0
  102. package/es/utils/{crypto-ts.js → crypto-ts.mjs} +11 -3
  103. package/es/utils/crypto-ts.mjs.map +1 -0
  104. package/es/utils/{crypto.js → crypto.mjs} +40 -105
  105. package/es/utils/crypto.mjs.map +1 -0
  106. package/es/utils/errors.mjs +689 -0
  107. package/es/utils/errors.mjs.map +1 -0
  108. package/es/utils/{hd-wallet.js → hd-wallet.mjs} +33 -34
  109. package/es/utils/hd-wallet.mjs.map +1 -0
  110. package/es/utils/{json-big.js → json-big.mjs} +1 -1
  111. package/es/utils/{json-big.js.map → json-big.mjs.map} +1 -1
  112. package/es/utils/{keystore.js → keystore.mjs} +63 -51
  113. package/es/utils/keystore.mjs.map +1 -0
  114. package/es/utils/{mptree.js → mptree.mjs} +21 -12
  115. package/es/utils/mptree.mjs.map +1 -0
  116. package/es/utils/{other.js → other.mjs} +2 -2
  117. package/es/utils/other.mjs.map +1 -0
  118. package/es/utils/{semver-satisfies.js → semver-satisfies.mjs} +1 -1
  119. package/es/utils/{semver-satisfies.js.map → semver-satisfies.mjs.map} +1 -1
  120. package/es/utils/{string.js → string.mjs} +1 -1
  121. package/es/utils/{string.js.map → string.mjs.map} +1 -1
  122. package/es/utils/{swagger.js → swagger.mjs} +46 -14
  123. package/es/utils/swagger.mjs.map +1 -0
  124. package/package.json +46 -55
  125. package/src/account/memory.js +13 -8
  126. package/src/account/multiple.js +12 -4
  127. package/src/ae/aens.js +12 -17
  128. package/src/ae/aepp.js +2 -2
  129. package/src/ae/contract.js +15 -152
  130. package/src/ae/index.js +2 -1
  131. package/src/ae/oracle.js +25 -10
  132. package/src/ae/universal.js +2 -2
  133. package/src/ae/wallet.js +2 -2
  134. package/src/chain/index.js +21 -2
  135. package/src/chain/node.js +80 -53
  136. package/src/channel/handlers.js +20 -17
  137. package/src/channel/internal.js +30 -24
  138. package/src/contract/{aci/index.js → aci.js} +152 -93
  139. package/src/contract/compiler.js +22 -117
  140. package/src/contract/ga/helpers.js +7 -5
  141. package/src/contract/ga/index.js +27 -24
  142. package/src/index.js +7 -8
  143. package/src/node-pool/index.js +9 -8
  144. package/src/node.js +5 -7
  145. package/src/tx/builder/field-types.js +61 -0
  146. package/src/tx/builder/helpers.js +113 -58
  147. package/src/tx/builder/index.js +39 -34
  148. package/src/tx/builder/schema.js +11 -25
  149. package/src/tx/tx-object.js +10 -9
  150. package/src/tx/tx.js +17 -8
  151. package/src/tx/validator.js +25 -5
  152. package/src/utils/aepp-wallet-communication/connection/browser-runtime.js +8 -3
  153. package/src/utils/aepp-wallet-communication/connection/browser-window-message.js +10 -6
  154. package/src/utils/aepp-wallet-communication/content-script-bridge.js +5 -4
  155. package/src/utils/aepp-wallet-communication/helpers.js +2 -12
  156. package/src/utils/aepp-wallet-communication/rpc/aepp-rpc.js +81 -55
  157. package/src/utils/aepp-wallet-communication/rpc/rpc-client.js +15 -6
  158. package/src/utils/aepp-wallet-communication/rpc/wallet-rpc.js +60 -49
  159. package/src/utils/aepp-wallet-communication/schema.ts +80 -0
  160. package/src/utils/aepp-wallet-communication/wallet-detector.js +3 -2
  161. package/src/utils/amount-formatter.ts +4 -3
  162. package/src/utils/async-init.js +10 -6
  163. package/src/utils/bignumber.ts +1 -9
  164. package/src/utils/bytes.ts +3 -2
  165. package/src/utils/crypto-ts.ts +2 -2
  166. package/src/utils/crypto.js +9 -92
  167. package/src/utils/errors.ts +683 -0
  168. package/src/utils/hd-wallet.js +11 -26
  169. package/src/utils/keystore.js +47 -51
  170. package/src/utils/mptree.ts +12 -6
  171. package/src/utils/swagger.js +67 -44
  172. package/CHANGELOG.md +0 -1603
  173. package/es/account/base.js.map +0 -1
  174. package/es/account/memory.js.map +0 -1
  175. package/es/account/multiple.js.map +0 -1
  176. package/es/ae/aens.js.map +0 -1
  177. package/es/ae/aepp.js.map +0 -1
  178. package/es/ae/contract.js +0 -312
  179. package/es/ae/contract.js.map +0 -1
  180. package/es/ae/index.js.map +0 -1
  181. package/es/ae/oracle.js.map +0 -1
  182. package/es/ae/universal.js.map +0 -1
  183. package/es/ae/wallet.js.map +0 -1
  184. package/es/chain/index.js.map +0 -1
  185. package/es/chain/node.js.map +0 -1
  186. package/es/channel/handlers.js.map +0 -1
  187. package/es/channel/index.js.map +0 -1
  188. package/es/channel/internal.js.map +0 -1
  189. package/es/contract/aci/helpers.js +0 -298
  190. package/es/contract/aci/helpers.js.map +0 -1
  191. package/es/contract/aci/index.js +0 -361
  192. package/es/contract/aci/index.js.map +0 -1
  193. package/es/contract/aci/transformation.js +0 -66
  194. package/es/contract/aci/transformation.js.map +0 -1
  195. package/es/contract/compiler.js +0 -244
  196. package/es/contract/compiler.js.map +0 -1
  197. package/es/contract/ga/helpers.js +0 -22
  198. package/es/contract/ga/helpers.js.map +0 -1
  199. package/es/contract/ga/index.js.map +0 -1
  200. package/es/contract/index.js +0 -162
  201. package/es/contract/index.js.map +0 -1
  202. package/es/index.js +0 -57
  203. package/es/index.js.map +0 -1
  204. package/es/node-pool/index.js.map +0 -1
  205. package/es/node.js.map +0 -1
  206. package/es/tx/builder/helpers.js.map +0 -1
  207. package/es/tx/builder/index.js.map +0 -1
  208. package/es/tx/builder/schema.js.map +0 -1
  209. package/es/tx/index.js.map +0 -1
  210. package/es/tx/tx-object.js.map +0 -1
  211. package/es/tx/tx.js.map +0 -1
  212. package/es/tx/validator.js.map +0 -1
  213. package/es/typings/blakejs/index.d.js +0 -2
  214. package/es/utils/aepp-wallet-communication/connection/browser-runtime.js.map +0 -1
  215. package/es/utils/aepp-wallet-communication/connection/browser-window-message.js.map +0 -1
  216. package/es/utils/aepp-wallet-communication/connection/index.js.map +0 -1
  217. package/es/utils/aepp-wallet-communication/content-script-bridge.js.map +0 -1
  218. package/es/utils/aepp-wallet-communication/helpers.js.map +0 -1
  219. package/es/utils/aepp-wallet-communication/rpc/aepp-rpc.js.map +0 -1
  220. package/es/utils/aepp-wallet-communication/rpc/rpc-client.js.map +0 -1
  221. package/es/utils/aepp-wallet-communication/rpc/wallet-rpc.js.map +0 -1
  222. package/es/utils/aepp-wallet-communication/schema.js +0 -107
  223. package/es/utils/aepp-wallet-communication/schema.js.map +0 -1
  224. package/es/utils/aepp-wallet-communication/wallet-detector.js.map +0 -1
  225. package/es/utils/amount-formatter.js.map +0 -1
  226. package/es/utils/async-init.js.map +0 -1
  227. package/es/utils/bignumber.js.map +0 -1
  228. package/es/utils/bytes.js.map +0 -1
  229. package/es/utils/crypto-ts.js.map +0 -1
  230. package/es/utils/crypto.js.map +0 -1
  231. package/es/utils/hd-wallet.js.map +0 -1
  232. package/es/utils/keystore.js.map +0 -1
  233. package/es/utils/mptree.js.map +0 -1
  234. package/es/utils/other.js.map +0 -1
  235. package/es/utils/swagger.js.map +0 -1
  236. package/src/contract/aci/transformation.js +0 -72
  237. package/src/contract/index.js +0 -163
  238. package/src/utils/aepp-wallet-communication/schema.js +0 -108
@@ -15,7 +15,7 @@
15
15
  * PERFORMANCE OF THIS SOFTWARE.
16
16
  */
17
17
 
18
- import { generateKeyPair, encodeContractAddress, encodeBase64Check } from '../utils/crypto'
18
+ import { generateKeyPair, encodeContractAddress } from '../utils/crypto'
19
19
  import {
20
20
  options,
21
21
  changeStatus,
@@ -28,20 +28,23 @@ import {
28
28
  fsmId
29
29
  } from './internal'
30
30
  import { unpackTx, buildTx } from '../tx/builder'
31
-
32
- function encodeRlpTx (rlpBinary) {
33
- return `tx_${encodeBase64Check(rlpBinary)}`
34
- }
31
+ import { encode } from '../tx/builder/helpers'
32
+ import {
33
+ IllegalArgumentError,
34
+ InsufficientBalanceError,
35
+ ChannelConnectionError,
36
+ UnexpectedChannelMessageError
37
+ } from '../utils/errors'
35
38
 
36
39
  async function appendSignature (tx, signFn) {
37
40
  const { signatures, encodedTx } = unpackTx(tx).tx
38
- const result = await signFn(encodeRlpTx(encodedTx.rlpEncoded))
41
+ const result = await signFn(encode(encodedTx.rlpEncoded, 'tx'))
39
42
  if (typeof result === 'string') {
40
43
  const { tx: signedTx, txType } = unpackTx(result)
41
- return encodeRlpTx(buildTx({
44
+ return buildTx({
42
45
  signatures: signatures.concat(signedTx.signatures),
43
46
  encodedTx: signedTx.encodedTx.rlpEncoded
44
- }, txType).rlpEncoded)
47
+ }, txType).tx
45
48
  }
46
49
  return result
47
50
  }
@@ -49,7 +52,7 @@ async function appendSignature (tx, signFn) {
49
52
  function handleUnexpectedMessage (channel, message, state) {
50
53
  if (state && state.reject) {
51
54
  state.reject(Object.assign(
52
- new Error(`Unexpected message received:\n\n${JSON.stringify(message)}`),
55
+ new UnexpectedChannelMessageError(`Unexpected message received:\n\n${JSON.stringify(message)}`),
53
56
  { wsMessage: message }
54
57
  ))
55
58
  }
@@ -75,7 +78,7 @@ export function awaitingConnection (channel, message, state) {
75
78
  return { handler: awaitingConnection }
76
79
  }
77
80
  if (message.method === 'channels.error') {
78
- emit(channel, 'error', new Error(message.payload.message))
81
+ emit(channel, 'error', new ChannelConnectionError(message.payload.message))
79
82
  return { handler: channelClosed }
80
83
  }
81
84
  }
@@ -251,17 +254,17 @@ export async function awaitingOffChainTx (channel, message, state) {
251
254
  }
252
255
  }
253
256
  if (message.method === 'channels.error') {
254
- state.reject(new Error(message.data.message))
257
+ state.reject(new ChannelConnectionError(message.data.message))
255
258
  return { handler: channelOpen }
256
259
  }
257
260
  if (message.error) {
258
261
  const { data = [] } = message.error
259
262
  if (data.find(i => i.code === 1001)) {
260
- state.reject(new Error('Insufficient balance'))
263
+ state.reject(new InsufficientBalanceError('Insufficient balance'))
261
264
  } else if (data.find(i => i.code === 1002)) {
262
- state.reject(new Error('Amount cannot be negative'))
265
+ state.reject(new IllegalArgumentError('Amount cannot be negative'))
263
266
  } else {
264
- state.reject(new Error(message.error.message))
267
+ state.reject(new ChannelConnectionError(message.error.message))
265
268
  }
266
269
  return { handler: channelOpen }
267
270
  }
@@ -303,7 +306,7 @@ export function awaitingOffChainUpdate (channel, message, state) {
303
306
  }
304
307
  }
305
308
  if (message.error) {
306
- state.reject(new Error(message.error.message))
309
+ state.reject(new ChannelConnectionError(message.error.message))
307
310
  return { handler: channelOpen }
308
311
  }
309
312
  return handleUnexpectedMessage(channel, message, state)
@@ -387,7 +390,7 @@ export function awaitingLeave (channel, message, state) {
387
390
  return { handler: channelClosed }
388
391
  }
389
392
  if (message.method === 'channels.error') {
390
- state.reject(new Error(message.data.message))
393
+ state.reject(new ChannelConnectionError(message.data.message))
391
394
  return { handler: channelOpen }
392
395
  }
393
396
  return handleUnexpectedMessage(channel, message, state)
@@ -651,7 +654,7 @@ export function awaitingCallsPruned (channels, message, state) {
651
654
  state.resolve()
652
655
  return { handler: channelOpen }
653
656
  }
654
- state.reject(new Error('Unexpected message received'))
657
+ state.reject(new UnexpectedChannelMessageError('Unexpected message received'))
655
658
  return { handler: channelClosed }
656
659
  }
657
660
 
@@ -20,6 +20,7 @@ import { EventEmitter } from 'events'
20
20
  import JsonBig from '../utils/json-big'
21
21
  import { pascalToSnake } from '../utils/string'
22
22
  import { awaitingConnection, awaitingReconnection, channelOpen } from './handlers'
23
+ import { ChannelCallError, ChannelPingTimedOutError, UnknownChannelStateError } from '../utils/errors'
23
24
 
24
25
  // Send ping message every 10 seconds
25
26
  const PING_TIMEOUT_MS = 10000
@@ -49,7 +50,7 @@ export function emit (channel, ...args) {
49
50
 
50
51
  function enterState (channel, nextState) {
51
52
  if (!nextState) {
52
- throw new Error('State Channels FSM entered unknown state')
53
+ throw new UnknownChannelStateError()
53
54
  }
54
55
  fsm.set(channel, nextState)
55
56
  if (nextState.handler.enter) {
@@ -109,16 +110,21 @@ async function handleMessage (channel, message) {
109
110
  }
110
111
 
111
112
  async function dequeueMessage (channel) {
112
- const queue = messageQueue.get(channel)
113
- if (messageQueueLocked.get(channel) || !queue.length) {
114
- return
115
- }
116
- const [message, ...remaining] = queue
117
- messageQueue.set(channel, remaining || [])
113
+ if (messageQueueLocked.get(channel)) return
114
+ const messages = messageQueue.get(channel)
115
+ if (!messages.length) return
118
116
  messageQueueLocked.set(channel, true)
119
- await handleMessage(channel, message)
117
+ while (messages.length) {
118
+ const message = messages.shift()
119
+ try {
120
+ await handleMessage(channel, message)
121
+ } catch (error) {
122
+ console.error('Error handling incoming message:')
123
+ console.error(message)
124
+ console.error(error)
125
+ }
126
+ }
120
127
  messageQueueLocked.set(channel, false)
121
- dequeueMessage(channel)
122
128
  }
123
129
 
124
130
  function ping (channel) {
@@ -134,7 +140,7 @@ function ping (channel) {
134
140
  })
135
141
  pongTimeoutId.set(channel, setTimeout(() => {
136
142
  disconnect(channel)
137
- emit(channel, 'error', new Error('Server pong timed out'))
143
+ emit(channel, 'error', new ChannelPingTimedOutError())
138
144
  }, PONG_TIMEOUT_MS))
139
145
  }, PING_TIMEOUT_MS))
140
146
  }
@@ -150,9 +156,13 @@ function onMessage (channel, data) {
150
156
  } finally {
151
157
  rpcCallbacks.get(channel).delete(message.id)
152
158
  }
153
- } else if (message.method === 'channels.message') {
159
+ return
160
+ }
161
+ if (message.method === 'channels.message') {
154
162
  emit(channel, 'message', message.params.data.message)
155
- } else if (message.method === 'channels.system.pong') {
163
+ return
164
+ }
165
+ if (message.method === 'channels.system.pong') {
156
166
  if (
157
167
  (message.params.channel_id === channelId.get(channel)) ||
158
168
  // Skip channelId check if channelId is not known yet
@@ -160,18 +170,10 @@ function onMessage (channel, data) {
160
170
  ) {
161
171
  ping(channel)
162
172
  }
163
- } else {
164
- messageQueue.set(channel, [...(messageQueue.get(channel) || []), message])
165
- dequeueMessage(channel)
166
- }
167
- }
168
-
169
- function wrapCallErrorMessage (message) {
170
- const [{ message: details } = {}] = message.error.data || []
171
- if (details) {
172
- return new Error(`${message.error.message}: ${details}`)
173
+ return
173
174
  }
174
- return new Error(message.error.message)
175
+ messageQueue.get(channel).push(message)
176
+ dequeueMessage(channel)
175
177
  }
176
178
 
177
179
  export function call (channel, method, params) {
@@ -179,7 +181,10 @@ export function call (channel, method, params) {
179
181
  const id = sequence.set(channel, sequence.get(channel) + 1).get(channel)
180
182
  rpcCallbacks.get(channel).set(id, (message) => {
181
183
  if (message.result) return resolve(message.result)
182
- if (message.error) return reject(wrapCallErrorMessage(message))
184
+ if (message.error) {
185
+ const [{ message: details } = {}] = message.error.data || []
186
+ return reject(new ChannelCallError(message.error.message + (details ? `: ${details}` : '')))
187
+ }
183
188
  })
184
189
  send(channel, { jsonrpc: '2.0', method, id, params })
185
190
  })
@@ -199,6 +204,7 @@ export async function initialize (channel, { url, ...channelOptions }) {
199
204
  eventEmitters.set(channel, new EventEmitter())
200
205
  sequence.set(channel, 0)
201
206
  rpcCallbacks.set(channel, new Map())
207
+ messageQueue.set(channel, [])
202
208
 
203
209
  const wsUrl = new URL(url)
204
210
  Object.entries(channelOptions)
@@ -22,38 +22,28 @@
22
22
  * @export getContractInstance
23
23
  */
24
24
 
25
- import * as R from 'ramda'
26
- import BigNumber from 'bignumber.js'
27
25
  import { Encoder as Calldata } from '@aeternity/aepp-calldata'
28
- import { decodeEvents } from './transformation'
29
- import { DRY_RUN_ACCOUNT, DEPOSIT } from '../../tx/builder/schema'
30
- import TxObject from '../../tx/tx-object'
31
- import { decode } from '../../tx/builder/helpers'
32
-
33
- /**
34
- * Get function schema from contract ACI object
35
- * @param {Object} aci Contract ACI object
36
- * @param {String} name Function name
37
- * @param external
38
- * @return {Object} function ACI
39
- */
40
- function getFunctionACI (aci, name, external) {
41
- const fn = aci.functions.find(f => f.name === name)
42
- if (!fn && name !== 'init') throw new Error(`Function ${name} doesn't exist in contract`)
43
-
44
- return {
45
- ...fn,
46
- bindings: [
47
- {
48
- state: aci.state,
49
- type_defs: aci.type_defs,
50
- name: aci.name
51
- },
52
- ...external.map(R.pick(['state', 'type_defs', 'name']))
53
- ],
54
- event: aci.event ? aci.event.variant : []
55
- }
56
- }
26
+ import { DRY_RUN_ACCOUNT, GAS_MAX } from '../tx/builder/schema'
27
+ import TxObject from '../tx/tx-object'
28
+ import { decode } from '../tx/builder/helpers'
29
+ import {
30
+ MissingContractDefError,
31
+ MissingContractAddressError,
32
+ InactiveContractError,
33
+ BytecodeMismatchError,
34
+ InternalError,
35
+ DuplicateContractError,
36
+ MissingFunctionNameError,
37
+ InvalidMethodInvocationError,
38
+ NotPayableFunctionError,
39
+ TypeError,
40
+ NodeInvocationError,
41
+ IllegalArgumentError,
42
+ NoSuchContractFunctionError,
43
+ MissingEventDefinitionError,
44
+ AmbiguousEventDefinitionError
45
+ } from '../utils/errors'
46
+ import { hash } from '../utils/crypto-ts'
57
47
 
58
48
  /**
59
49
  * Generate contract ACI object with predefined js methods for contract usage - can be used for
@@ -68,7 +58,7 @@ function getFunctionACI (aci, name, external) {
68
58
  * @param {Boolean} [options.validateBytecode] Compare source code with on-chain version
69
59
  * @return {ContractInstance} JS Contract API
70
60
  * @example
71
- * const contractIns = await client.getContractInstance({ source })
61
+ * const contractIns = await aeSdk.getContractInstance({ source })
72
62
  * await contractIns.deploy([321]) or await contractIns.methods.init(321)
73
63
  * const callResult = await contractIns.call('setState', [123]) or
74
64
  * await contractIns.methods.setState.send(123, options)
@@ -81,30 +71,32 @@ function getFunctionACI (aci, name, external) {
81
71
  export default async function getContractInstance ({
82
72
  source,
83
73
  bytecode,
84
- aci,
74
+ aci: _aci,
85
75
  contractAddress,
86
76
  filesystem = {},
87
77
  validateBytecode,
88
78
  ...otherOptions
89
79
  } = {}) {
90
- aci = aci || (source && await this.contractGetACI(source, { filesystem }))
91
- if (!aci) throw new Error('Either ACI or source code is required')
80
+ if (!_aci && source) {
81
+ _aci = await this.compilerApi.generateACI({ code: source, options: { filesystem } })
82
+ }
83
+ if (!_aci) throw new MissingContractDefError()
92
84
  contractAddress = contractAddress && await this.resolveName(
93
85
  contractAddress, 'contract_pubkey', { resolveByNode: true }
94
86
  )
95
- if (!contractAddress && !source && !bytecode) throw new Error('Can\'t create instance by ACI without address')
87
+ if (!contractAddress && !source && !bytecode) {
88
+ throw new MissingContractAddressError('Can\'t create instance by ACI without address')
89
+ }
96
90
 
97
91
  if (contractAddress) {
98
- const contract = await this.getContract(contractAddress).catch(() => null)
99
- if (!contract) throw new Error(`Contract with address ${contractAddress} not found on-chain`)
100
- if (!contract.active) throw new Error(`Contract with address ${contractAddress} not active`)
92
+ const contract = await this.getContract(contractAddress)
93
+ if (!contract.active) throw new InactiveContractError(contractAddress)
101
94
  }
102
95
 
103
96
  const instance = {
104
- interface: R.defaultTo(null, R.prop('interface', aci)),
105
- aci: R.defaultTo(null, R.path(['encoded_aci', 'contract'], aci)),
106
- calldata: new Calldata([aci.encoded_aci, ...aci.external_encoded_aci]),
107
- externalAci: (aci.external_encoded_aci ?? []).map(a => a.contract || a.namespace),
97
+ _aci,
98
+ _name: _aci.encoded_aci.contract.name,
99
+ calldata: new Calldata([_aci.encoded_aci, ..._aci.external_encoded_aci]),
108
100
  source,
109
101
  bytecode,
110
102
  deployInfo: { address: contractAddress },
@@ -118,12 +110,14 @@ export default async function getContractInstance ({
118
110
  }
119
111
 
120
112
  if (validateBytecode) {
121
- if (!contractAddress) throw new Error('Can\'t validate bytecode without contract address')
113
+ if (!contractAddress) throw new MissingContractAddressError('Can\'t validate bytecode without contract address')
122
114
  const onChanBytecode = (await this.getContractByteCode(contractAddress)).bytecode
123
- const isValid = (source && await this
124
- .validateByteCodeAPI(onChanBytecode, source, instance.options).catch(() => false)) ||
125
- bytecode === onChanBytecode
126
- if (!isValid) throw new Error(`Contract ${source ? 'source' : 'bytecode'} do not correspond to the bytecode deployed on the chain`)
115
+ const isValid = source
116
+ ? await this.compilerApi.validateByteCode(
117
+ { bytecode: onChanBytecode, source, options: instance.options }
118
+ ).then((res) => Object.entries(res).length === 0, () => false)
119
+ : bytecode === onChanBytecode
120
+ if (!isValid) throw new BytecodeMismatchError(source ? 'source' : 'bytecode')
127
121
  }
128
122
 
129
123
  /**
@@ -133,11 +127,11 @@ export default async function getContractInstance ({
133
127
  * @return {String} bytecode bytecode
134
128
  */
135
129
  instance.compile = async (options = {}) => {
136
- if (instance.bytecode) throw new Error('Contract already compiled')
137
- if (!instance.source) throw new Error('Can\'t compile without source code')
138
- const { bytecode } = await this.contractCompile(
139
- instance.source, { ...instance.options, ...options }
140
- )
130
+ if (instance.bytecode) throw new IllegalArgumentError('Contract already compiled')
131
+ if (!instance.source) throw new IllegalArgumentError('Can\'t compile without source code')
132
+ const { bytecode } = await this.compilerApi.compileContract({
133
+ code: instance.source, options: { ...instance.options, ...options }
134
+ })
141
135
  instance.bytecode = bytecode
142
136
  return instance.bytecode
143
137
  }
@@ -148,9 +142,10 @@ export default async function getContractInstance ({
148
142
  hash: txData.hash, tx: TxObject({ tx: txData.rawTx }), txData, rawTx: txData.rawTx
149
143
  }
150
144
  if (options.waitMined === false) return result
151
- const txInfo = await this.getTxInfo(txData.hash)
152
- await handleCallError(txInfo)
153
- return { ...result, result: txInfo }
145
+ const { callInfo } = await this.api.getTransactionInfoByHash(txData.hash)
146
+ Object.assign(result.txData, callInfo) // TODO: don't duplicate data in result
147
+ await handleCallError(callInfo)
148
+ return { ...result, result: callInfo }
154
149
  }
155
150
 
156
151
  const handleCallError = ({ returnType, returnValue }) => {
@@ -164,9 +159,16 @@ export default async function getContractInstance ({
164
159
  case 'error':
165
160
  message = decode(returnValue).toString()
166
161
  break
167
- default: throw new Error(`Unknown returnType: ${returnType}`)
162
+ default: throw new InternalError(`Unknown returnType: ${returnType}`)
168
163
  }
169
- throw new Error(`Invocation failed${message ? `: "${message}"` : ''}`)
164
+ throw new NodeInvocationError(message)
165
+ }
166
+
167
+ instance._estimateGas = async (name, params, options) => {
168
+ const { result: { gasUsed } } =
169
+ await instance.call(name, params, { ...options, callStatic: true })
170
+ // taken from https://github.com/aeternity/aepp-sdk-js/issues/1286#issuecomment-977814771
171
+ return Math.floor(gasUsed * 1.25)
170
172
  }
171
173
 
172
174
  /**
@@ -178,15 +180,16 @@ export default async function getContractInstance ({
178
180
  * @return {Object} deploy info
179
181
  */
180
182
  instance.deploy = async (params = [], options) => {
181
- const opt = { ...instance.options, ...options, deposit: DEPOSIT }
183
+ const opt = { ...instance.options, ...options }
182
184
  if (!instance.bytecode) await instance.compile(opt)
183
185
  if (opt.callStatic) return instance.call('init', params, opt)
184
- if (instance.deployInfo.address) throw new Error('Contract already deployed')
186
+ if (instance.deployInfo.address) throw new DuplicateContractError()
185
187
 
186
188
  const ownerId = await this.address(opt)
187
189
  const { tx, contractId } = await this.contractCreateTx({
188
190
  ...opt,
189
- callData: instance.calldata.encode(instance.aci.name, 'init', params),
191
+ gas: opt.gas ?? await instance._estimateGas('init', params, opt),
192
+ callData: instance.calldata.encode(instance._name, 'init', params),
190
193
  code: instance.bytecode,
191
194
  ownerId
192
195
  })
@@ -197,16 +200,23 @@ export default async function getContractInstance ({
197
200
  transaction: hash,
198
201
  rawTx,
199
202
  txData,
200
- address: contractId,
201
- call: (name, args, options) =>
202
- this.contractCall(instance.source, contractId, name, args, { ...opt, ...options }),
203
- callStatic: (name, args, options) =>
204
- this.contractCallStatic(instance.source, contractId, name, args, { ...opt, ...options }),
205
- createdAt: new Date()
203
+ address: contractId
206
204
  })
207
205
  return instance.deployInfo
208
206
  }
209
207
 
208
+ /**
209
+ * Get function schema from contract ACI object
210
+ * @param {String} name Function name
211
+ * @return {Object} function ACI
212
+ */
213
+ function getFunctionACI (name) {
214
+ const fn = instance._aci.encoded_aci.contract.functions.find(f => f.name === name)
215
+ if (fn) return fn
216
+ if (name === 'init') return { payable: false }
217
+ throw new NoSuchContractFunctionError(`Function ${name} doesn't exist in contract`)
218
+ }
219
+
210
220
  /**
211
221
  * Call contract function
212
222
  * @alias module:@aeternity/aepp-sdk/es/contract/aci
@@ -219,22 +229,19 @@ export default async function getContractInstance ({
219
229
  */
220
230
  instance.call = async (fn, params = [], options = {}) => {
221
231
  const opt = { ...instance.options, ...options }
222
- const fnACI = getFunctionACI(instance.aci, fn, instance.externalAci)
232
+ const fnACI = getFunctionACI(fn)
223
233
  const contractId = instance.deployInfo.address
224
234
 
225
- if (!fn) throw new Error('Function name is required')
226
- if (fn === 'init' && !opt.callStatic) throw new Error('"init" can be called only via dryRun')
227
- if (!contractId && fn !== 'init') throw new Error('You need to deploy contract before calling!')
228
- if (
229
- BigNumber(opt.amount).gt(0) &&
230
- (Object.prototype.hasOwnProperty.call(fnACI, 'payable') && !fnACI.payable)
231
- ) throw new Error(`You try to pay "${opt.amount}" to function "${fn}" which is not payable. Only payable function can accept tokens`)
235
+ if (!fn) throw new MissingFunctionNameError()
236
+ if (fn === 'init' && !opt.callStatic) throw new InvalidMethodInvocationError('"init" can be called only via dryRun')
237
+ if (!contractId && fn !== 'init') throw new InvalidMethodInvocationError('You need to deploy contract before calling!')
238
+ if (opt.amount > 0 && fnACI.payable === false) throw new NotPayableFunctionError(`You try to pay "${opt.amount}" to function "${fn}" which is not payable. Only payable function can accept coins`)
232
239
 
233
240
  const callerId = await this.address(opt).catch(error => {
234
241
  if (opt.callStatic) return DRY_RUN_ACCOUNT.pub
235
242
  else throw error
236
243
  })
237
- const callData = instance.calldata.encode(instance.aci.name, fn, params)
244
+ const callData = instance.calldata.encode(instance._name, fn, params)
238
245
 
239
246
  let res
240
247
  if (opt.callStatic) {
@@ -243,8 +250,10 @@ export default async function getContractInstance ({
243
250
  }
244
251
  const txOpt = {
245
252
  ...opt,
253
+ gas: opt.gas ?? GAS_MAX,
246
254
  callData,
247
- nonce: opt.top && (await this.getAccount(callerId, { hash: opt.top })).nonce + 1
255
+ nonce: opt.nonce ??
256
+ (opt.top && (await this.getAccount(callerId, { hash: opt.top })).nonce + 1)
248
257
  }
249
258
  const tx = fn === 'init'
250
259
  ? (await this.contractCreateTx({ ...txOpt, code: instance.bytecode, ownerId: callerId })).tx
@@ -254,33 +263,84 @@ export default async function getContractInstance ({
254
263
  await handleCallError(callObj)
255
264
  res = { ...dryRunOther, tx: TxObject({ tx }), result: callObj }
256
265
  } else {
257
- const tx = await this.contractCallTx({ ...opt, callerId, contractId, callData })
266
+ const tx = await this.contractCallTx({
267
+ ...opt,
268
+ gas: opt.gas ?? await instance._estimateGas(fn, params, opt),
269
+ callerId,
270
+ contractId,
271
+ callData
272
+ })
258
273
  res = await sendAndProcess(tx, opt)
259
274
  }
260
275
  if (opt.waitMined || opt.callStatic) {
261
276
  res.decodedResult = fnACI.returns && fnACI.returns !== 'unit' && fn !== 'init' &&
262
- instance.calldata.decode(instance.aci.name, fn, res.result.returnValue)
263
- res.decodedEvents = instance.decodeEvents(res.result.log)
277
+ instance.calldata.decode(instance._name, fn, res.result.returnValue)
278
+ res.decodedEvents = instance.decodeEvents(res.result.log, opt)
264
279
  }
265
280
  return res
266
281
  }
267
282
 
283
+ /**
284
+ * @param {String} address Contract address that emitted event
285
+ * @param {BigInt} nameHash Hash of emitted event name
286
+ * @param {Object} [options]
287
+ * @param {Object} [options.contractAddressToName] Map of contract addresses to their names
288
+ * @return {String} Contract name
289
+ * @throws {MissingEventDefinitionError}
290
+ * @throws {AmbiguousEventDefinitionError}
291
+ */
292
+ function getContractNameByEvent (address, nameHash, { contractAddressToName }) {
293
+ const addressToName = { ...instance.options.contractAddressToName, ...contractAddressToName }
294
+ if (addressToName[address]) return addressToName[address]
295
+
296
+ const matchedEvents = [
297
+ instance._aci.encoded_aci,
298
+ ...instance._aci.external_encoded_aci
299
+ ]
300
+ .filter(({ contract }) => contract?.event)
301
+ .map(({ contract }) => [contract.name, contract.event.variant])
302
+ .map(([name, events]) => events.map(event => [name, Object.keys(event)[0]]))
303
+ .flat()
304
+ .filter(([, eventName]) => BigInt('0x' + hash(eventName).toString('hex')) === nameHash)
305
+ switch (matchedEvents.length) {
306
+ case 0: throw new MissingEventDefinitionError(nameHash, address)
307
+ case 1: return matchedEvents[0][0]
308
+ default: throw new AmbiguousEventDefinitionError(address, matchedEvents)
309
+ }
310
+ }
311
+
268
312
  /**
269
313
  * Decode Events
270
314
  * @alias module:@aeternity/aepp-sdk/es/contract/aci
271
315
  * @rtype (events: Array) => DecodedEvents: Array
272
- * @param {Array} events Array of encoded events(callRes.result.log)
316
+ * @param {Array} events Array of encoded events (callRes.result.log)
317
+ * @param {Object} [options]
318
+ * @param {Boolean} [options.omitUnknown] Omit events that can't be decoded in case ACI is not
319
+ * complete
273
320
  * @return {Object} DecodedEvents
274
321
  */
275
- instance.decodeEvents = (events) => {
276
- const eventsACI = instance.aci.event ? instance.aci.event.variant : []
277
- const eventsSchema = eventsACI.map(e => {
278
- const name = Object.keys(e)[0]
279
- return { name, types: e[name] }
322
+ instance.decodeEvents = (events, { omitUnknown, ...opt } = {}) => events
323
+ .map(event => {
324
+ const topics = event.topics.map(t => BigInt(t))
325
+ let contractName
326
+ try {
327
+ contractName = getContractNameByEvent(event.address, topics[0], opt)
328
+ } catch (error) {
329
+ if (omitUnknown && error instanceof MissingEventDefinitionError) return null
330
+ throw error
331
+ }
332
+ const decoded = instance.calldata.decodeEvent(contractName, event.data, topics)
333
+ const [name, args] = Object.entries(decoded)[0]
334
+ return {
335
+ name,
336
+ args,
337
+ contract: {
338
+ name: contractName,
339
+ address: event.address
340
+ }
341
+ }
280
342
  })
281
-
282
- return decodeEvents(events, eventsSchema)
283
- }
343
+ .filter(e => e)
284
344
 
285
345
  /**
286
346
  * Generate proto function based on contract function using Contract ACI schema
@@ -291,11 +351,11 @@ export default async function getContractInstance ({
291
351
  * `await contract.methods.testFunction.get()` -> use call-static(dry-run)
292
352
  * `await contract.methods.testFunction.send()` -> send tx on-chain
293
353
  */
294
- instance.methods = Object.fromEntries(instance.aci.functions
354
+ instance.methods = Object.fromEntries(instance._aci.encoded_aci.contract.functions
295
355
  .map(({ name, arguments: aciArgs, stateful }) => {
296
356
  const genHandler = callStatic => (...args) => {
297
357
  const options = args.length === aciArgs.length + 1 ? args.pop() : {}
298
- if (typeof options !== 'object') throw new Error(`Options should be an object: ${options}`)
358
+ if (typeof options !== 'object') throw new TypeError(`Options should be an object: ${options}`)
299
359
  if (name === 'init') return instance.deploy(args, { callStatic, ...options })
300
360
  return instance.call(name, args, { callStatic, ...options })
301
361
  }
@@ -305,8 +365,7 @@ export default async function getContractInstance ({
305
365
  genHandler(name === 'init' ? false : !stateful),
306
366
  {
307
367
  get: genHandler(true),
308
- send: genHandler(false),
309
- decodeEvents: events => instance.decodeEvents(events)
368
+ send: genHandler(false)
310
369
  }
311
370
  )
312
371
  ]