Source

js-stellar-base/src/sorobandata_builder.js

  1. import xdr from './xdr';
  2. /**
  3. * Supports building {@link xdr.SorobanTransactionData} structures with various
  4. * items set to specific values.
  5. *
  6. * This is recommended for when you are building
  7. * {@link Operation.extendFootprintTtl} / {@link Operation.restoreFootprint}
  8. * operations and need to {@link TransactionBuilder.setSorobanData} to avoid
  9. * (re)building the entire data structure from scratch.
  10. *
  11. * @constructor
  12. *
  13. * @param {string | xdr.SorobanTransactionData} [sorobanData] either a
  14. * base64-encoded string that represents an
  15. * {@link xdr.SorobanTransactionData} instance or an XDR instance itself
  16. * (it will be copied); if omitted or "falsy" (e.g. an empty string), it
  17. * starts with an empty instance
  18. *
  19. * @example
  20. * // You want to use an existing data blob but override specific parts.
  21. * const newData = new SorobanDataBuilder(existing)
  22. * .setReadOnly(someLedgerKeys)
  23. * .setRefundableFee("1000")
  24. * .build();
  25. *
  26. * // You want an instance from scratch
  27. * const newData = new SorobanDataBuilder()
  28. * .setFootprint([someLedgerKey], [])
  29. * .setRefundableFee("1000")
  30. * .build();
  31. */
  32. export class SorobanDataBuilder {
  33. _data;
  34. constructor(sorobanData) {
  35. let data;
  36. if (!sorobanData) {
  37. data = new xdr.SorobanTransactionData({
  38. resources: new xdr.SorobanResources({
  39. footprint: new xdr.LedgerFootprint({ readOnly: [], readWrite: [] }),
  40. instructions: 0,
  41. readBytes: 0,
  42. writeBytes: 0
  43. }),
  44. ext: new xdr.ExtensionPoint(0),
  45. resourceFee: new xdr.Int64(0)
  46. });
  47. } else if (
  48. typeof sorobanData === 'string' ||
  49. ArrayBuffer.isView(sorobanData)
  50. ) {
  51. data = SorobanDataBuilder.fromXDR(sorobanData);
  52. } else {
  53. data = SorobanDataBuilder.fromXDR(sorobanData.toXDR()); // copy
  54. }
  55. this._data = data;
  56. }
  57. /**
  58. * Decodes and builds a {@link xdr.SorobanTransactionData} instance.
  59. * @param {Uint8Array|Buffer|string} data raw input to decode
  60. * @returns {xdr.SorobanTransactionData}
  61. */
  62. static fromXDR(data) {
  63. return xdr.SorobanTransactionData.fromXDR(
  64. data,
  65. typeof data === 'string' ? 'base64' : 'raw'
  66. );
  67. }
  68. /**
  69. * Sets the resource fee portion of the Soroban data.
  70. * @param {number | bigint | string} fee the resource fee to set (int64)
  71. * @returns {SorobanDataBuilder}
  72. */
  73. setResourceFee(fee) {
  74. this._data.resourceFee(new xdr.Int64(fee));
  75. return this;
  76. }
  77. /**
  78. * Sets up the resource metrics.
  79. *
  80. * You should almost NEVER need this, as its often generated / provided to you
  81. * by transaction simulation/preflight from a Soroban RPC server.
  82. *
  83. * @param {number} cpuInstrs number of CPU instructions
  84. * @param {number} readBytes number of bytes being read
  85. * @param {number} writeBytes number of bytes being written
  86. *
  87. * @returns {SorobanDataBuilder}
  88. */
  89. setResources(cpuInstrs, readBytes, writeBytes) {
  90. this._data.resources().instructions(cpuInstrs);
  91. this._data.resources().readBytes(readBytes);
  92. this._data.resources().writeBytes(writeBytes);
  93. return this;
  94. }
  95. /**
  96. * Appends the given ledger keys to the existing storage access footprint.
  97. * @param {xdr.LedgerKey[]} readOnly read-only keys to add
  98. * @param {xdr.LedgerKey[]} readWrite read-write keys to add
  99. * @returns {SorobanDataBuilder} this builder instance
  100. */
  101. appendFootprint(readOnly, readWrite) {
  102. return this.setFootprint(
  103. this.getReadOnly().concat(readOnly),
  104. this.getReadWrite().concat(readWrite)
  105. );
  106. }
  107. /**
  108. * Sets the storage access footprint to be a certain set of ledger keys.
  109. *
  110. * You can also set each field explicitly via
  111. * {@link SorobanDataBuilder.setReadOnly} and
  112. * {@link SorobanDataBuilder.setReadWrite} or add to the existing footprint
  113. * via {@link SorobanDataBuilder.appendFootprint}.
  114. *
  115. * Passing `null|undefined` to either parameter will IGNORE the existing
  116. * values. If you want to clear them, pass `[]`, instead.
  117. *
  118. * @param {xdr.LedgerKey[]|null} [readOnly] the set of ledger keys to set in
  119. * the read-only portion of the transaction's `sorobanData`, or `null |
  120. * undefined` to keep the existing keys
  121. * @param {xdr.LedgerKey[]|null} [readWrite] the set of ledger keys to set in
  122. * the read-write portion of the transaction's `sorobanData`, or `null |
  123. * undefined` to keep the existing keys
  124. * @returns {SorobanDataBuilder} this builder instance
  125. */
  126. setFootprint(readOnly, readWrite) {
  127. if (readOnly !== null) {
  128. // null means "leave me alone"
  129. this.setReadOnly(readOnly);
  130. }
  131. if (readWrite !== null) {
  132. this.setReadWrite(readWrite);
  133. }
  134. return this;
  135. }
  136. /**
  137. * @param {xdr.LedgerKey[]} readOnly read-only keys in the access footprint
  138. * @returns {SorobanDataBuilder}
  139. */
  140. setReadOnly(readOnly) {
  141. this._data
  142. .resources()
  143. .footprint()
  144. .readOnly(readOnly ?? []);
  145. return this;
  146. }
  147. /**
  148. * @param {xdr.LedgerKey[]} readWrite read-write keys in the access footprint
  149. * @returns {SorobanDataBuilder}
  150. */
  151. setReadWrite(readWrite) {
  152. this._data
  153. .resources()
  154. .footprint()
  155. .readWrite(readWrite ?? []);
  156. return this;
  157. }
  158. /**
  159. * @returns {xdr.SorobanTransactionData} a copy of the final data structure
  160. */
  161. build() {
  162. return xdr.SorobanTransactionData.fromXDR(this._data.toXDR()); // clone
  163. }
  164. //
  165. // getters follow
  166. //
  167. /** @returns {xdr.LedgerKey[]} the read-only storage access pattern */
  168. getReadOnly() {
  169. return this.getFootprint().readOnly();
  170. }
  171. /** @returns {xdr.LedgerKey[]} the read-write storage access pattern */
  172. getReadWrite() {
  173. return this.getFootprint().readWrite();
  174. }
  175. /** @returns {xdr.LedgerFootprint} the storage access pattern */
  176. getFootprint() {
  177. return this._data.resources().footprint();
  178. }
  179. }