Home Reference Source

packages/oo7-substrate/src/transact.js

  1. const { Bond } = require('oo7')
  2. const { blake2b } = require('blakejs')
  3. const { SubscriptionBond } = require('./subscriptionBond')
  4. const { encode } = require('./codec')
  5. const { secretStore } = require('./secretStore')
  6. const { TransactionEra, AccountIndex } = require('./types')
  7. const { runtimeUp, runtime, chain } = require('./bonds')
  8. const { bytesToHex } = require('./utils')
  9.  
  10. class TransactionBond extends SubscriptionBond {
  11. constructor (data) {
  12. super('author_submitAndWatchExtrinsic', ['0x' + bytesToHex(data)], null, {sending: true})
  13. }
  14. }
  15.  
  16. function composeTransaction (sender, call, index, era, checkpoint, senderAccount, compact) {
  17. return new Promise((resolve, reject) => {
  18. if (typeof sender == 'string') {
  19. sender = ss58Decode(sender)
  20. }
  21. if (sender instanceof Uint8Array && sender.length == 32) {
  22. senderAccount = sender
  23. } else if (!senderAccount) {
  24. reject(`Invalid senderAccount when sender is account index`)
  25. }
  26. console.log("composing transaction", senderAccount, index, call, era, checkpoint);
  27. let e = encode([
  28. index, call, era, checkpoint
  29. ], [
  30. 'Compact<Index>', 'Call', 'TransactionEra', 'Hash'
  31. ])
  32.  
  33. let legacy = runtime.version.isReady() && (
  34. runtime.version._value.specName == 'node' && runtime.version._value.specVersion < 17
  35. || runtime.version._value.specName == 'polkadot' && runtime.version._value.specVersion < 107
  36. )
  37. if (!legacy && e.length > 256) {
  38. console.log(`Oversize transaction (length ${e.length} bytes). Hashing.`)
  39. e = blake2b(e, null, 32)
  40. }
  41. let signature = secretStore().sign(senderAccount, e)
  42. console.log("encoding transaction", sender, index, era, call);
  43. let signedData = encode(encode({
  44. _type: 'Transaction',
  45. version: 0x81,
  46. sender,
  47. signature,
  48. index,
  49. era,
  50. call
  51. }), 'Vec<u8>')
  52. console.log("signed:", bytesToHex(signedData))
  53. setTimeout(() => resolve(signedData), 1000)
  54. })
  55. }
  56.  
  57. // tx = {
  58. // sender
  59. // call
  60. // longevity?
  61. // index?
  62. // }
  63. function post(tx) {
  64. return Bond.all([tx, chain.height, runtimeUp]).map(([o, height, unused]) => {
  65. let {sender, call, index, longevity, compact} = o
  66. // defaults
  67. longevity = typeof longevity === 'undefined' ? 256 : longevity
  68. compact = typeof compact === 'undefined' ? true : compact
  69. let senderIsIndex = typeof sender === 'number' || sender instanceof AccountIndex
  70.  
  71. let senderAccount = senderIsIndex
  72. ? runtime.indices.lookup(sender)
  73. : sender
  74. if (senderIsIndex && !compact) {
  75. sender = senderAccount
  76. }
  77. let era
  78. let eraHash
  79. if (longevity === true) {
  80. era = new TransactionEra;
  81. eraHash = chain.hash(0)
  82. } else {
  83. // use longevity with height to determine era and eraHash
  84. let l = Math.min(15, Math.max(1, Math.ceil(Math.log2(longevity)) - 1))
  85. let period = 2 << l
  86. let factor = Math.max(1, period >> 12)
  87. let Q = (n, d) => Math.floor(n / d) * d
  88. let eraNumber = Q(height, factor)
  89. let phase = eraNumber % period
  90. era = new TransactionEra(period, phase)
  91. eraHash = chain.hash(eraNumber)
  92. }
  93. return {
  94. sender,
  95. call,
  96. era,
  97. eraHash,
  98. index: index || runtime.system.accountNonce(senderAccount),
  99. senderAccount,
  100. compact
  101. }
  102. }, 2).latched(false).map(o =>
  103. o && composeTransaction(o.sender, o.call, o.index, o.era, o.eraHash, o.senderAccount, o.compact)
  104. ).map(composed => {
  105. return composed ? new TransactionBond(composed) : { signing: true }
  106. })
  107. }
  108.  
  109. module.exports = { composeTransaction, post };