Source

js-stellar-base/src/memo.js

  1. import { UnsignedHyper } from '@stellar/js-xdr';
  2. import BigNumber from './util/bignumber';
  3. import xdr from './xdr';
  4. /**
  5. * Type of {@link Memo}.
  6. */
  7. export const MemoNone = 'none';
  8. /**
  9. * Type of {@link Memo}.
  10. */
  11. export const MemoID = 'id';
  12. /**
  13. * Type of {@link Memo}.
  14. */
  15. export const MemoText = 'text';
  16. /**
  17. * Type of {@link Memo}.
  18. */
  19. export const MemoHash = 'hash';
  20. /**
  21. * Type of {@link Memo}.
  22. */
  23. export const MemoReturn = 'return';
  24. /**
  25. * `Memo` represents memos attached to transactions.
  26. *
  27. * @param {string} type - `MemoNone`, `MemoID`, `MemoText`, `MemoHash` or `MemoReturn`
  28. * @param {*} value - `string` for `MemoID`, `MemoText`, buffer of hex string for `MemoHash` or `MemoReturn`
  29. * @see [Transactions concept](https://developers.stellar.org/docs/glossary/transactions/)
  30. * @class Memo
  31. */
  32. export class Memo {
  33. constructor(type, value = null) {
  34. this._type = type;
  35. this._value = value;
  36. switch (this._type) {
  37. case MemoNone:
  38. break;
  39. case MemoID:
  40. Memo._validateIdValue(value);
  41. break;
  42. case MemoText:
  43. Memo._validateTextValue(value);
  44. break;
  45. case MemoHash:
  46. case MemoReturn:
  47. Memo._validateHashValue(value);
  48. // We want MemoHash and MemoReturn to have Buffer as a value
  49. if (typeof value === 'string') {
  50. this._value = Buffer.from(value, 'hex');
  51. }
  52. break;
  53. default:
  54. throw new Error('Invalid memo type');
  55. }
  56. }
  57. /**
  58. * Contains memo type: `MemoNone`, `MemoID`, `MemoText`, `MemoHash` or `MemoReturn`
  59. */
  60. get type() {
  61. return this._type;
  62. }
  63. set type(type) {
  64. throw new Error('Memo is immutable');
  65. }
  66. /**
  67. * Contains memo value:
  68. * * `null` for `MemoNone`,
  69. * * `string` for `MemoID`,
  70. * * `Buffer` for `MemoText` after decoding using `fromXDRObject`, original value otherwise,
  71. * * `Buffer` for `MemoHash`, `MemoReturn`.
  72. */
  73. get value() {
  74. switch (this._type) {
  75. case MemoNone:
  76. return null;
  77. case MemoID:
  78. case MemoText:
  79. return this._value;
  80. case MemoHash:
  81. case MemoReturn:
  82. return Buffer.from(this._value);
  83. default:
  84. throw new Error('Invalid memo type');
  85. }
  86. }
  87. set value(value) {
  88. throw new Error('Memo is immutable');
  89. }
  90. static _validateIdValue(value) {
  91. const error = new Error(`Expects a int64 as a string. Got ${value}`);
  92. if (typeof value !== 'string') {
  93. throw error;
  94. }
  95. let number;
  96. try {
  97. number = new BigNumber(value);
  98. } catch (e) {
  99. throw error;
  100. }
  101. // Infinity
  102. if (!number.isFinite()) {
  103. throw error;
  104. }
  105. // NaN
  106. if (number.isNaN()) {
  107. throw error;
  108. }
  109. }
  110. static _validateTextValue(value) {
  111. if (!xdr.Memo.armTypeForArm('text').isValid(value)) {
  112. throw new Error('Expects string, array or buffer, max 28 bytes');
  113. }
  114. }
  115. static _validateHashValue(value) {
  116. const error = new Error(
  117. `Expects a 32 byte hash value or hex encoded string. Got ${value}`
  118. );
  119. if (value === null || typeof value === 'undefined') {
  120. throw error;
  121. }
  122. let valueBuffer;
  123. if (typeof value === 'string') {
  124. if (!/^[0-9A-Fa-f]{64}$/g.test(value)) {
  125. throw error;
  126. }
  127. valueBuffer = Buffer.from(value, 'hex');
  128. } else if (Buffer.isBuffer(value)) {
  129. valueBuffer = Buffer.from(value);
  130. } else {
  131. throw error;
  132. }
  133. if (!valueBuffer.length || valueBuffer.length !== 32) {
  134. throw error;
  135. }
  136. }
  137. /**
  138. * Returns an empty memo (`MemoNone`).
  139. * @returns {Memo}
  140. */
  141. static none() {
  142. return new Memo(MemoNone);
  143. }
  144. /**
  145. * Creates and returns a `MemoText` memo.
  146. * @param {string} text - memo text
  147. * @returns {Memo}
  148. */
  149. static text(text) {
  150. return new Memo(MemoText, text);
  151. }
  152. /**
  153. * Creates and returns a `MemoID` memo.
  154. * @param {string} id - 64-bit number represented as a string
  155. * @returns {Memo}
  156. */
  157. static id(id) {
  158. return new Memo(MemoID, id);
  159. }
  160. /**
  161. * Creates and returns a `MemoHash` memo.
  162. * @param {array|string} hash - 32 byte hash or hex encoded string
  163. * @returns {Memo}
  164. */
  165. static hash(hash) {
  166. return new Memo(MemoHash, hash);
  167. }
  168. /**
  169. * Creates and returns a `MemoReturn` memo.
  170. * @param {array|string} hash - 32 byte hash or hex encoded string
  171. * @returns {Memo}
  172. */
  173. static return(hash) {
  174. return new Memo(MemoReturn, hash);
  175. }
  176. /**
  177. * Returns XDR memo object.
  178. * @returns {xdr.Memo}
  179. */
  180. toXDRObject() {
  181. switch (this._type) {
  182. case MemoNone:
  183. return xdr.Memo.memoNone();
  184. case MemoID:
  185. return xdr.Memo.memoId(UnsignedHyper.fromString(this._value));
  186. case MemoText:
  187. return xdr.Memo.memoText(this._value);
  188. case MemoHash:
  189. return xdr.Memo.memoHash(this._value);
  190. case MemoReturn:
  191. return xdr.Memo.memoReturn(this._value);
  192. default:
  193. return null;
  194. }
  195. }
  196. /**
  197. * Returns {@link Memo} from XDR memo object.
  198. * @param {xdr.Memo} object XDR memo object
  199. * @returns {Memo}
  200. */
  201. static fromXDRObject(object) {
  202. switch (object.arm()) {
  203. case 'id':
  204. return Memo.id(object.value().toString());
  205. case 'text':
  206. return Memo.text(object.value());
  207. case 'hash':
  208. return Memo.hash(object.value());
  209. case 'retHash':
  210. return Memo.return(object.value());
  211. default:
  212. break;
  213. }
  214. if (typeof object.value() === 'undefined') {
  215. return Memo.none();
  216. }
  217. throw new Error('Unknown type');
  218. }
  219. }