@usearete/sdk 0.1.5 → 0.2.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.
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var pako = require('pako');
4
+ var ed25519 = require('@noble/ed25519');
4
5
 
5
6
  const DEFAULT_MAX_ENTRIES_PER_VIEW = 10000;
6
7
  const DEFAULT_CONFIG = {
@@ -187,6 +188,11 @@ function isHostedAreteWebsocketUrl(websocketUrl) {
187
188
  return false;
188
189
  }
189
190
  }
191
+ /**
192
+ * Historical sentinel accepted for back-compat: connecting with this URL is
193
+ * treated the same as passing no WebSocket URL at all (HTTP-only mode).
194
+ */
195
+ const DISABLED_WEBSOCKET_URL = 'ws://127.0.0.1/__arete_disabled__';
190
196
  class ConnectionManager {
191
197
  constructor(config) {
192
198
  this.ws = null;
@@ -202,11 +208,11 @@ class ConnectionManager {
202
208
  this.stateHandlers = new Set();
203
209
  this.socketIssueHandlers = new Set();
204
210
  this.reconnectForTokenRefresh = false;
205
- if (!config.websocketUrl) {
206
- throw new AreteError('websocketUrl is required', 'INVALID_CONFIG');
207
- }
208
- this.websocketUrl = config.websocketUrl;
209
- this.hostedAreteUrl = isHostedAreteWebsocketUrl(config.websocketUrl);
211
+ const websocketUrl = config.websocketUrl && config.websocketUrl !== DISABLED_WEBSOCKET_URL
212
+ ? config.websocketUrl
213
+ : null;
214
+ this.websocketUrl = websocketUrl;
215
+ this.hostedAreteUrl = websocketUrl !== null && isHostedAreteWebsocketUrl(websocketUrl);
210
216
  this.reconnectIntervals = config.reconnectIntervals ?? DEFAULT_CONFIG.reconnectIntervals;
211
217
  this.maxReconnectAttempts =
212
218
  config.maxReconnectAttempts ?? DEFAULT_CONFIG.maxReconnectAttempts;
@@ -298,7 +304,7 @@ class ConnectionManager {
298
304
  }
299
305
  createTokenEndpointRequestBody() {
300
306
  return {
301
- websocket_url: this.websocketUrl,
307
+ websocket_url: this.websocketUrl ?? '',
302
308
  };
303
309
  }
304
310
  async fetchTokenFromEndpoint(tokenEndpoint) {
@@ -452,14 +458,21 @@ class ConnectionManager {
452
458
  this.ws.close(1000, 'token refresh');
453
459
  }
454
460
  buildAuthUrl(token) {
461
+ const websocketUrl = this.requireWebsocketUrl();
455
462
  if (this.authConfig?.tokenTransport === 'bearer') {
456
- return this.websocketUrl;
463
+ return websocketUrl;
457
464
  }
458
465
  if (!token) {
459
- return this.websocketUrl;
466
+ return websocketUrl;
467
+ }
468
+ const separator = websocketUrl.includes('?') ? '&' : '?';
469
+ return `${websocketUrl}${separator}${DEFAULT_QUERY_PARAMETER}=${encodeURIComponent(token)}`;
470
+ }
471
+ requireWebsocketUrl() {
472
+ if (this.websocketUrl === null) {
473
+ throw new AreteError('WebSocket transport is disabled (client was connected with transport: "http"); views and subscriptions are unavailable', 'WEBSOCKET_DISABLED');
460
474
  }
461
- const separator = this.websocketUrl.includes('?') ? '&' : '?';
462
- return `${this.websocketUrl}${separator}${DEFAULT_QUERY_PARAMETER}=${encodeURIComponent(token)}`;
475
+ return this.websocketUrl;
463
476
  }
464
477
  createWebSocket(url, token) {
465
478
  if (this.authConfig?.tokenTransport === 'bearer') {
@@ -479,6 +492,12 @@ class ConnectionManager {
479
492
  getState() {
480
493
  return this.currentState;
481
494
  }
495
+ async getHttpAuthToken(forceRefresh = false) {
496
+ return this.getOrRefreshToken(forceRefresh);
497
+ }
498
+ clearHttpAuthToken() {
499
+ this.clearTokenState();
500
+ }
482
501
  onFrame(handler) {
483
502
  this.frameHandlers.add(handler);
484
503
  return () => {
@@ -514,6 +533,7 @@ class ConnectionManager {
514
533
  return issue;
515
534
  }
516
535
  async connect() {
536
+ this.requireWebsocketUrl();
517
537
  if (this.ws?.readyState === WebSocket.OPEN ||
518
538
  this.ws?.readyState === WebSocket.CONNECTING ||
519
539
  this.currentState === 'connecting') {
@@ -658,6 +678,7 @@ class ConnectionManager {
658
678
  }
659
679
  }
660
680
  subscribe(subscription) {
681
+ this.requireWebsocketUrl();
661
682
  const subKey = this.makeSubKey(subscription);
662
683
  if (this.currentState === 'connected' && this.ws?.readyState === WebSocket.OPEN) {
663
684
  if (this.activeSubscriptions.has(subKey)) {
@@ -761,16 +782,20 @@ class ConnectionManager {
761
782
  }
762
783
  }
763
784
 
764
- function isObject$1(item) {
785
+ const INTERNAL_SEQ_FIELD = '__seq';
786
+ function isObject(item) {
765
787
  return item !== null && typeof item === 'object' && !Array.isArray(item);
766
788
  }
767
- function deepMergeWithAppend$1(target, source, appendPaths, currentPath = '') {
768
- if (!isObject$1(target) || !isObject$1(source)) {
789
+ function deepMergeWithAppend(target, source, appendPaths, currentPath = '') {
790
+ if (!isObject(target) || !isObject(source)) {
769
791
  return source;
770
792
  }
771
793
  const result = { ...target };
772
794
  for (const key in source) {
773
795
  const sourceValue = source[key];
796
+ if (sourceValue === undefined) {
797
+ continue;
798
+ }
774
799
  const targetValue = result[key];
775
800
  const fieldPath = currentPath ? `${currentPath}.${key}` : key;
776
801
  if (Array.isArray(sourceValue) && Array.isArray(targetValue)) {
@@ -781,8 +806,8 @@ function deepMergeWithAppend$1(target, source, appendPaths, currentPath = '') {
781
806
  result[key] = sourceValue;
782
807
  }
783
808
  }
784
- else if (isObject$1(sourceValue) && isObject$1(targetValue)) {
785
- result[key] = deepMergeWithAppend$1(targetValue, sourceValue, appendPaths, fieldPath);
809
+ else if (isObject(sourceValue) && isObject(targetValue)) {
810
+ result[key] = deepMergeWithAppend(targetValue, sourceValue, appendPaths, fieldPath);
786
811
  }
787
812
  else {
788
813
  result[key] = sourceValue;
@@ -790,6 +815,36 @@ function deepMergeWithAppend$1(target, source, appendPaths, currentPath = '') {
790
815
  }
791
816
  return result;
792
817
  }
818
+ function stripUndefinedProperties(value) {
819
+ if (Array.isArray(value)) {
820
+ return value.map(item => stripUndefinedProperties(item));
821
+ }
822
+ if (!isObject(value)) {
823
+ return value;
824
+ }
825
+ const result = {};
826
+ for (const [key, nestedValue] of Object.entries(value)) {
827
+ if (nestedValue === undefined) {
828
+ continue;
829
+ }
830
+ result[key] = stripUndefinedProperties(nestedValue);
831
+ }
832
+ return result;
833
+ }
834
+ function toCamelCaseSegment(value) {
835
+ if (value === '_seq') {
836
+ return INTERNAL_SEQ_FIELD;
837
+ }
838
+ const pascal = value
839
+ .split(/[_.-]/)
840
+ .filter(segment => segment.length > 0)
841
+ .map(segment => segment[0].toUpperCase() + segment.slice(1))
842
+ .join('');
843
+ if (pascal.length === 0) {
844
+ return value;
845
+ }
846
+ return pascal[0].toLowerCase() + pascal.slice(1);
847
+ }
793
848
  class FrameProcessor {
794
849
  constructor(storage, config = {}) {
795
850
  this.pendingUpdates = [];
@@ -801,9 +856,10 @@ class FrameProcessor {
801
856
  : config.maxEntriesPerView;
802
857
  this.flushIntervalMs = config.flushIntervalMs ?? 0;
803
858
  this.schemas = config.schemas;
859
+ this.patchSchemas = config.patchSchemas;
804
860
  }
805
- getSchema(viewPath) {
806
- const schemas = this.schemas;
861
+ getSchema(viewPath, patch = false) {
862
+ const schemas = patch ? this.patchSchemas : this.schemas;
807
863
  if (!schemas)
808
864
  return null;
809
865
  const entityName = viewPath.split('/')[0];
@@ -812,19 +868,86 @@ class FrameProcessor {
812
868
  const entityKey = entityName;
813
869
  return schemas[entityKey] ?? null;
814
870
  }
815
- validateEntity(viewPath, data) {
816
- const schema = this.getSchema(viewPath);
871
+ normalizeEntity(viewPath, data, patch = false) {
872
+ const schema = this.getSchema(viewPath, patch)
873
+ ?? (!patch ? null : this.getSchema(viewPath));
817
874
  if (!schema)
818
- return true;
875
+ return data;
819
876
  const result = schema.safeParse(data);
820
877
  if (!result.success) {
821
878
  console.warn('[Arete] Frame validation failed:', {
822
879
  view: viewPath,
823
880
  error: result.error,
824
881
  });
825
- return false;
882
+ return null;
826
883
  }
827
- return true;
884
+ return stripUndefinedProperties(result.data);
885
+ }
886
+ hasSchema(viewPath) {
887
+ return this.getSchema(viewPath) !== null;
888
+ }
889
+ normalizePathSegment(viewPath, segment) {
890
+ if (!this.hasSchema(viewPath)) {
891
+ return segment;
892
+ }
893
+ return toCamelCaseSegment(segment);
894
+ }
895
+ normalizePath(viewPath, path) {
896
+ if (!this.hasSchema(viewPath)) {
897
+ return path;
898
+ }
899
+ return path
900
+ .split('.')
901
+ .filter(segment => segment.length > 0)
902
+ .map(segment => this.normalizePathSegment(viewPath, segment))
903
+ .join('.');
904
+ }
905
+ normalizeSortConfig(viewPath, sort) {
906
+ if (!this.hasSchema(viewPath)) {
907
+ return sort;
908
+ }
909
+ return {
910
+ ...sort,
911
+ field: sort.field.map(segment => this.normalizePathSegment(viewPath, segment)),
912
+ };
913
+ }
914
+ normalizeAppendPaths(viewPath, append) {
915
+ if (!append || append.length === 0 || !this.hasSchema(viewPath)) {
916
+ return append ?? [];
917
+ }
918
+ return append.map(path => this.normalizePath(viewPath, path));
919
+ }
920
+ extractSeq(data) {
921
+ if (!isObject(data)) {
922
+ return undefined;
923
+ }
924
+ const seq = data._seq;
925
+ if (typeof seq === 'string') {
926
+ return seq;
927
+ }
928
+ if (typeof seq === 'number' && Number.isFinite(seq)) {
929
+ return String(seq);
930
+ }
931
+ return undefined;
932
+ }
933
+ getInternalSeq(data) {
934
+ if (!isObject(data)) {
935
+ return undefined;
936
+ }
937
+ const seq = data[INTERNAL_SEQ_FIELD];
938
+ return typeof seq === 'string' ? seq : undefined;
939
+ }
940
+ attachInternalSeq(viewPath, data, seq) {
941
+ if (!seq || !this.hasSchema(viewPath) || !isObject(data)) {
942
+ return data;
943
+ }
944
+ Object.defineProperty(data, INTERNAL_SEQ_FIELD, {
945
+ value: seq,
946
+ enumerable: false,
947
+ configurable: true,
948
+ writable: true,
949
+ });
950
+ return data;
828
951
  }
829
952
  handleFrame(frame) {
830
953
  if (this.flushIntervalMs === 0) {
@@ -910,7 +1033,9 @@ class FrameProcessor {
910
1033
  }
911
1034
  handleSubscribedFrame(frame) {
912
1035
  if (this.storage.setViewConfig && frame.sort) {
913
- this.storage.setViewConfig(frame.view, { sort: frame.sort });
1036
+ this.storage.setViewConfig(frame.view, {
1037
+ sort: this.normalizeSortConfig(frame.view, frame.sort),
1038
+ });
914
1039
  }
915
1040
  }
916
1041
  handleSnapshotFrame(frame) {
@@ -920,17 +1045,19 @@ class FrameProcessor {
920
1045
  handleSnapshotFrameWithoutEnforce(frame) {
921
1046
  const viewPath = frame.entity;
922
1047
  for (const entity of frame.data) {
923
- if (!this.validateEntity(viewPath, entity.data)) {
1048
+ const normalized = this.normalizeEntity(viewPath, entity.data);
1049
+ if (normalized === null) {
924
1050
  continue;
925
1051
  }
1052
+ const nextValue = this.attachInternalSeq(viewPath, normalized, this.extractSeq(entity.data));
926
1053
  const previousValue = this.storage.get(viewPath, entity.key);
927
- this.storage.set(viewPath, entity.key, entity.data);
1054
+ this.storage.set(viewPath, entity.key, nextValue);
928
1055
  this.storage.notifyUpdate(viewPath, entity.key, {
929
1056
  type: 'upsert',
930
1057
  key: entity.key,
931
- data: entity.data,
1058
+ data: nextValue,
932
1059
  });
933
- this.emitRichUpdate(viewPath, entity.key, previousValue, entity.data, 'upsert');
1060
+ this.emitRichUpdate(viewPath, entity.key, previousValue, nextValue, 'upsert');
934
1061
  }
935
1062
  }
936
1063
  handleEntityFrame(frame) {
@@ -943,33 +1070,39 @@ class FrameProcessor {
943
1070
  switch (frame.op) {
944
1071
  case 'create':
945
1072
  case 'upsert':
946
- if (!this.validateEntity(viewPath, frame.data)) {
1073
+ {
1074
+ const normalized = this.normalizeEntity(viewPath, frame.data);
1075
+ if (normalized === null) {
1076
+ break;
1077
+ }
1078
+ const nextValue = this.attachInternalSeq(viewPath, normalized, frame.seq ?? this.extractSeq(frame.data));
1079
+ this.storage.set(viewPath, frame.key, nextValue);
1080
+ this.storage.notifyUpdate(viewPath, frame.key, {
1081
+ type: 'upsert',
1082
+ key: frame.key,
1083
+ data: nextValue,
1084
+ });
1085
+ this.emitRichUpdate(viewPath, frame.key, previousValue, nextValue, frame.op);
947
1086
  break;
948
1087
  }
949
- this.storage.set(viewPath, frame.key, frame.data);
950
- this.storage.notifyUpdate(viewPath, frame.key, {
951
- type: 'upsert',
952
- key: frame.key,
953
- data: frame.data,
954
- });
955
- this.emitRichUpdate(viewPath, frame.key, previousValue, frame.data, frame.op);
956
- break;
957
1088
  case 'patch': {
958
1089
  const existing = this.storage.get(viewPath, frame.key);
959
- const appendPaths = frame.append ?? [];
960
- const merged = existing
961
- ? deepMergeWithAppend$1(existing, frame.data, appendPaths)
962
- : frame.data;
963
- if (!this.validateEntity(viewPath, merged)) {
1090
+ const normalizedPatch = this.normalizeEntity(viewPath, frame.data, true);
1091
+ if (normalizedPatch === null) {
964
1092
  break;
965
1093
  }
966
- this.storage.set(viewPath, frame.key, merged);
1094
+ const appendPaths = this.normalizeAppendPaths(viewPath, frame.append);
1095
+ const merged = existing
1096
+ ? deepMergeWithAppend(existing, normalizedPatch, appendPaths)
1097
+ : normalizedPatch;
1098
+ const nextValue = this.attachInternalSeq(viewPath, merged, frame.seq ?? this.extractSeq(frame.data) ?? this.getInternalSeq(existing));
1099
+ this.storage.set(viewPath, frame.key, nextValue);
967
1100
  this.storage.notifyUpdate(viewPath, frame.key, {
968
1101
  type: 'patch',
969
1102
  key: frame.key,
970
- data: frame.data,
1103
+ data: normalizedPatch,
971
1104
  });
972
- this.emitRichUpdate(viewPath, frame.key, previousValue, merged, 'patch', frame.data);
1105
+ this.emitRichUpdate(viewPath, frame.key, previousValue, nextValue, 'patch', normalizedPatch);
973
1106
  break;
974
1107
  }
975
1108
  case 'delete':
@@ -1002,7 +1135,7 @@ class FrameProcessor {
1002
1135
  }
1003
1136
  }
1004
1137
 
1005
- let ViewData$1 = class ViewData {
1138
+ class ViewData {
1006
1139
  constructor() {
1007
1140
  this.entities = new Map();
1008
1141
  this.accessOrder = [];
@@ -1056,7 +1189,7 @@ let ViewData$1 = class ViewData {
1056
1189
  this.entities.clear();
1057
1190
  this.accessOrder = [];
1058
1191
  }
1059
- };
1192
+ }
1060
1193
  class MemoryAdapter {
1061
1194
  constructor(_config = {}) {
1062
1195
  this.views = new Map();
@@ -1104,7 +1237,7 @@ class MemoryAdapter {
1104
1237
  set(viewPath, key, data) {
1105
1238
  let view = this.views.get(viewPath);
1106
1239
  if (!view) {
1107
- view = new ViewData$1();
1240
+ view = new ViewData();
1108
1241
  this.views.set(viewPath, view);
1109
1242
  }
1110
1243
  view.set(key, data);
@@ -1144,7 +1277,7 @@ class MemoryAdapter {
1144
1277
  }
1145
1278
  }
1146
1279
 
1147
- function getNestedValue$1(obj, path) {
1280
+ function getNestedValue(obj, path) {
1148
1281
  let current = obj;
1149
1282
  for (const segment of path) {
1150
1283
  if (current === null || current === undefined)
@@ -1155,16 +1288,25 @@ function getNestedValue$1(obj, path) {
1155
1288
  }
1156
1289
  return current;
1157
1290
  }
1158
- function compareSortValues$1(a, b) {
1291
+ function compareSortValues(a, b) {
1159
1292
  if (a === b)
1160
1293
  return 0;
1161
1294
  if (a === undefined || a === null)
1162
1295
  return -1;
1163
1296
  if (b === undefined || b === null)
1164
1297
  return 1;
1298
+ if (typeof a === 'bigint' && typeof b === 'bigint') {
1299
+ return a < b ? -1 : 1;
1300
+ }
1165
1301
  if (typeof a === 'number' && typeof b === 'number') {
1166
1302
  return a - b;
1167
1303
  }
1304
+ if (typeof a === 'bigint' && typeof b === 'number' && Number.isInteger(b)) {
1305
+ return a < BigInt(b) ? -1 : 1;
1306
+ }
1307
+ if (typeof a === 'number' && typeof b === 'bigint' && Number.isInteger(a)) {
1308
+ return BigInt(a) < b ? -1 : 1;
1309
+ }
1168
1310
  if (typeof a === 'string' && typeof b === 'string') {
1169
1311
  return a.localeCompare(b);
1170
1312
  }
@@ -1291,7 +1433,7 @@ class SortedStorageDecorator {
1291
1433
  sortedKeys.splice(insertIdx, 0, key);
1292
1434
  }
1293
1435
  binarySearchInsertPosition(viewPath, sortedKeys, sortConfig, newKey, newValue) {
1294
- const newSortValue = getNestedValue$1(newValue, sortConfig.field);
1436
+ const newSortValue = getNestedValue(newValue, sortConfig.field);
1295
1437
  const isDesc = sortConfig.order === 'desc';
1296
1438
  let low = 0;
1297
1439
  let high = sortedKeys.length;
@@ -1299,8 +1441,8 @@ class SortedStorageDecorator {
1299
1441
  const mid = Math.floor((low + high) / 2);
1300
1442
  const midKey = sortedKeys[mid];
1301
1443
  const midEntity = this.inner.get(viewPath, midKey);
1302
- const midValue = getNestedValue$1(midEntity, sortConfig.field);
1303
- let cmp = compareSortValues$1(newSortValue, midValue);
1444
+ const midValue = getNestedValue(midEntity, sortConfig.field);
1445
+ let cmp = compareSortValues(newSortValue, midValue);
1304
1446
  if (isDesc)
1305
1447
  cmp = -cmp;
1306
1448
  if (cmp === 0) {
@@ -1322,9 +1464,9 @@ class SortedStorageDecorator {
1322
1464
  const isDesc = sortConfig.order === 'desc';
1323
1465
  const entries = allKeys.map(k => [k, this.inner.get(viewPath, k)]);
1324
1466
  entries.sort((a, b) => {
1325
- const aValue = getNestedValue$1(a[1], sortConfig.field);
1326
- const bValue = getNestedValue$1(b[1], sortConfig.field);
1327
- let cmp = compareSortValues$1(aValue, bValue);
1467
+ const aValue = getNestedValue(a[1], sortConfig.field);
1468
+ const bValue = getNestedValue(b[1], sortConfig.field);
1469
+ let cmp = compareSortValues(aValue, bValue);
1328
1470
  if (isDesc)
1329
1471
  cmp = -cmp;
1330
1472
  if (cmp === 0) {
@@ -1654,6 +1796,160 @@ function createTypedViews(stack, storage, subscriptionRegistry) {
1654
1796
  return views;
1655
1797
  }
1656
1798
 
1799
+ class ReadRequestError extends Error {
1800
+ constructor(input) {
1801
+ super(`Read request to '${input.path}' failed (${input.status}): ${input.body}`);
1802
+ this.name = 'ReadRequestError';
1803
+ this.status = input.status;
1804
+ this.path = input.path;
1805
+ this.body = input.body;
1806
+ this.serverErrorCode = input.serverErrorCode;
1807
+ }
1808
+ }
1809
+ function getServerErrorCode(response, body) {
1810
+ const headerCode = response.headers.get('X-Error-Code');
1811
+ if (headerCode) {
1812
+ return headerCode;
1813
+ }
1814
+ try {
1815
+ const parsed = JSON.parse(body);
1816
+ return typeof parsed.code === 'string' ? parsed.code : undefined;
1817
+ }
1818
+ catch {
1819
+ return undefined;
1820
+ }
1821
+ }
1822
+ async function parseReadResponse(response, path) {
1823
+ if (!response.ok) {
1824
+ const body = await response.text();
1825
+ throw new ReadRequestError({
1826
+ status: response.status,
1827
+ path,
1828
+ body,
1829
+ serverErrorCode: getServerErrorCode(response, body),
1830
+ });
1831
+ }
1832
+ return response.json();
1833
+ }
1834
+ function programAccountRead(input) {
1835
+ return {
1836
+ account: input.account,
1837
+ path: input.path,
1838
+ schema: input.schema,
1839
+ };
1840
+ }
1841
+ function programQuery(input) {
1842
+ return {
1843
+ name: input.name,
1844
+ path: input.path,
1845
+ method: input.method,
1846
+ schema: input.schema,
1847
+ };
1848
+ }
1849
+ function stackQuery(input) {
1850
+ return {
1851
+ name: input.name,
1852
+ path: input.path,
1853
+ method: input.method,
1854
+ schema: input.schema,
1855
+ };
1856
+ }
1857
+
1858
+ function joinUrl(baseUrl, path) {
1859
+ return `${baseUrl.replace(/\/$/, '')}${path.startsWith('/') ? path : `/${path}`}`;
1860
+ }
1861
+ function decodeBase64(encoded) {
1862
+ if (typeof atob === 'function') {
1863
+ const binary = atob(encoded);
1864
+ const bytes = new Uint8Array(binary.length);
1865
+ for (let i = 0; i < binary.length; i++) {
1866
+ bytes[i] = binary.charCodeAt(i);
1867
+ }
1868
+ return bytes;
1869
+ }
1870
+ const bufferCtor = globalThis.Buffer;
1871
+ if (bufferCtor) {
1872
+ return new Uint8Array(bufferCtor.from(encoded, 'base64'));
1873
+ }
1874
+ throw new Error('No base64 decoder available in this environment');
1875
+ }
1876
+ function deriveHttpEndpoint(wsUrl) {
1877
+ try {
1878
+ const parsed = new URL(wsUrl);
1879
+ if (parsed.protocol === 'ws:') {
1880
+ parsed.protocol = 'http:';
1881
+ }
1882
+ else if (parsed.protocol === 'wss:') {
1883
+ parsed.protocol = 'https:';
1884
+ }
1885
+ return parsed.toString().replace(/\/$/, '');
1886
+ }
1887
+ catch {
1888
+ return wsUrl.replace(/^wss?:/i, (protocol) => protocol.toLowerCase() === 'wss:' ? 'https:' : 'http:');
1889
+ }
1890
+ }
1891
+ function createChainClient(httpBaseUrl, fetchImpl) {
1892
+ return {
1893
+ async exists(address) {
1894
+ const path = `/chain/exists/${encodeURIComponent(address)}`;
1895
+ const response = await fetchImpl(joinUrl(httpBaseUrl, path));
1896
+ const body = await parseReadResponse(response, path);
1897
+ return body.exists;
1898
+ },
1899
+ async lamports(address) {
1900
+ const path = `/chain/lamports/${encodeURIComponent(address)}`;
1901
+ const response = await fetchImpl(joinUrl(httpBaseUrl, path));
1902
+ const body = await parseReadResponse(response, path);
1903
+ return body.lamports;
1904
+ },
1905
+ async minimumBalanceForRentExemption(space) {
1906
+ const path = `/chain/rent-exemption/${encodeURIComponent(String(space))}`;
1907
+ const response = await fetchImpl(joinUrl(httpBaseUrl, path));
1908
+ const body = await parseReadResponse(response, path);
1909
+ return body.lamports;
1910
+ },
1911
+ async clock() {
1912
+ const path = '/chain/clock';
1913
+ const response = await fetchImpl(joinUrl(httpBaseUrl, path));
1914
+ return parseReadResponse(response, path);
1915
+ },
1916
+ async account(address) {
1917
+ const path = `/chain/accounts/${encodeURIComponent(address)}`;
1918
+ const response = await fetchImpl(joinUrl(httpBaseUrl, path));
1919
+ const body = await parseReadResponse(response, path);
1920
+ if (!body) {
1921
+ return null;
1922
+ }
1923
+ return {
1924
+ address: body.address,
1925
+ ownerProgram: body.ownerProgram,
1926
+ lamports: body.lamports,
1927
+ executable: body.executable,
1928
+ data: decodeBase64(body.data),
1929
+ };
1930
+ },
1931
+ async mint(address) {
1932
+ const path = `/chain/mints/${encodeURIComponent(address)}`;
1933
+ const response = await fetchImpl(joinUrl(httpBaseUrl, path));
1934
+ return parseReadResponse(response, path);
1935
+ },
1936
+ async tokenAccount(address) {
1937
+ const path = `/chain/token-accounts/${encodeURIComponent(address)}`;
1938
+ const response = await fetchImpl(joinUrl(httpBaseUrl, path));
1939
+ return parseReadResponse(response, path);
1940
+ },
1941
+ async balance(input) {
1942
+ const path = '/chain/balances';
1943
+ const response = await fetchImpl(joinUrl(httpBaseUrl, path), {
1944
+ method: 'POST',
1945
+ headers: { 'content-type': 'application/json' },
1946
+ body: JSON.stringify(input),
1947
+ });
1948
+ return parseReadResponse(response, path);
1949
+ },
1950
+ };
1951
+ }
1952
+
1657
1953
  /**
1658
1954
  * PDA (Program Derived Address) derivation utilities.
1659
1955
  *
@@ -1668,7 +1964,10 @@ function decodeBase58(str) {
1668
1964
  if (str.length === 0) {
1669
1965
  return new Uint8Array(0);
1670
1966
  }
1671
- const bytes = [0];
1967
+ // Big-endian byte accumulator (stored little-endian here, reversed at the
1968
+ // end). Must start empty: a leading `[0]` produces a spurious extra byte for
1969
+ // all-zero values such as the System Program ("111...1").
1970
+ const bytes = [];
1672
1971
  for (const char of str) {
1673
1972
  const value = BASE58_ALPHABET.indexOf(char);
1674
1973
  if (value === -1) {
@@ -1700,7 +1999,9 @@ function encodeBase58(bytes) {
1700
1999
  if (bytes.length === 0) {
1701
2000
  return '';
1702
2001
  }
1703
- const digits = [0];
2002
+ // Must start empty for the same reason as `decodeBase58`: a leading `[0]`
2003
+ // yields an extra '1' character when encoding all-zero inputs.
2004
+ const digits = [];
1704
2005
  for (const byte of bytes) {
1705
2006
  let carry = byte;
1706
2007
  for (let i = 0; i < digits.length; i++) {
@@ -1721,25 +2022,125 @@ function encodeBase58(bytes) {
1721
2022
  }
1722
2023
  return digits.reverse().map(d => BASE58_ALPHABET[d]).join('');
1723
2024
  }
2025
+ // SHA-256 round constants (first 32 bits of the fractional parts of the cube
2026
+ // roots of the first 64 primes).
2027
+ const SHA256_K = new Uint32Array([
2028
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
2029
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
2030
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
2031
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
2032
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
2033
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
2034
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
2035
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
2036
+ ]);
2037
+ /**
2038
+ * Dependency-free, synchronous SHA-256.
2039
+ *
2040
+ * Used so PDA derivation works identically in browsers, Node (both CJS and
2041
+ * ESM), and bundlers without relying on `require('crypto')` or async WebCrypto.
2042
+ */
2043
+ function sha256Pure(data) {
2044
+ const h = new Uint32Array([
2045
+ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
2046
+ 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
2047
+ ]);
2048
+ const bitLen = data.length * 8;
2049
+ // Pad: append 0x80, then zeros, until length ≡ 56 (mod 64), then 8-byte length.
2050
+ const paddedLen = ((data.length + 8) >> 6) * 64 + 64;
2051
+ const msg = new Uint8Array(paddedLen);
2052
+ msg.set(data);
2053
+ msg[data.length] = 0x80;
2054
+ // 64-bit big-endian bit length (high 32 bits assumed 0 for our seed sizes).
2055
+ const dv = new DataView(msg.buffer);
2056
+ dv.setUint32(paddedLen - 4, bitLen >>> 0, false);
2057
+ dv.setUint32(paddedLen - 8, Math.floor(bitLen / 0x100000000), false);
2058
+ const w = new Uint32Array(64);
2059
+ for (let offset = 0; offset < paddedLen; offset += 64) {
2060
+ for (let i = 0; i < 16; i++) {
2061
+ w[i] = dv.getUint32(offset + i * 4, false);
2062
+ }
2063
+ for (let i = 16; i < 64; i++) {
2064
+ const w15 = w[i - 15];
2065
+ const w2 = w[i - 2];
2066
+ const s0 = ((w15 >>> 7) | (w15 << 25)) ^ ((w15 >>> 18) | (w15 << 14)) ^ (w15 >>> 3);
2067
+ const s1 = ((w2 >>> 17) | (w2 << 15)) ^ ((w2 >>> 19) | (w2 << 13)) ^ (w2 >>> 10);
2068
+ w[i] = (w[i - 16] + s0 + w[i - 7] + s1) >>> 0;
2069
+ }
2070
+ let a = h[0], b = h[1], c = h[2], d = h[3];
2071
+ let e = h[4], f = h[5], g = h[6], hh = h[7];
2072
+ for (let i = 0; i < 64; i++) {
2073
+ const S1 = ((e >>> 6) | (e << 26)) ^ ((e >>> 11) | (e << 21)) ^ ((e >>> 25) | (e << 7));
2074
+ const ch = (e & f) ^ (~e & g);
2075
+ const t1 = (hh + S1 + ch + SHA256_K[i] + w[i]) >>> 0;
2076
+ const S0 = ((a >>> 2) | (a << 30)) ^ ((a >>> 13) | (a << 19)) ^ ((a >>> 22) | (a << 10));
2077
+ const maj = (a & b) ^ (a & c) ^ (b & c);
2078
+ const t2 = (S0 + maj) >>> 0;
2079
+ hh = g;
2080
+ g = f;
2081
+ f = e;
2082
+ e = (d + t1) >>> 0;
2083
+ d = c;
2084
+ c = b;
2085
+ b = a;
2086
+ a = (t1 + t2) >>> 0;
2087
+ }
2088
+ h[0] = (h[0] + a) >>> 0;
2089
+ h[1] = (h[1] + b) >>> 0;
2090
+ h[2] = (h[2] + c) >>> 0;
2091
+ h[3] = (h[3] + d) >>> 0;
2092
+ h[4] = (h[4] + e) >>> 0;
2093
+ h[5] = (h[5] + f) >>> 0;
2094
+ h[6] = (h[6] + g) >>> 0;
2095
+ h[7] = (h[7] + hh) >>> 0;
2096
+ }
2097
+ const out = new Uint8Array(32);
2098
+ const outView = new DataView(out.buffer);
2099
+ for (let i = 0; i < 8; i++) {
2100
+ outView.setUint32(i * 4, h[i], false);
2101
+ }
2102
+ return out;
2103
+ }
1724
2104
  /**
1725
- * SHA-256 hash function (synchronous, Node.js).
2105
+ * SHA-256 hash function (synchronous).
1726
2106
  */
1727
2107
  function sha256Sync(data) {
1728
- // eslint-disable-next-line @typescript-eslint/no-var-requires
1729
- const { createHash } = require('crypto');
1730
- return new Uint8Array(createHash('sha256').update(Buffer.from(data)).digest());
2108
+ return sha256Pure(data);
1731
2109
  }
1732
2110
  /**
1733
- * SHA-256 hash function (async, works in browser and Node.js).
2111
+ * SHA-256 hash function (async). Uses WebCrypto when available for speed,
2112
+ * otherwise falls back to the pure implementation.
1734
2113
  */
1735
2114
  async function sha256Async(data) {
1736
2115
  if (typeof globalThis !== 'undefined' && globalThis.crypto && globalThis.crypto.subtle) {
1737
- // Create a copy of the data to ensure we have an ArrayBuffer
1738
2116
  const copy = new Uint8Array(data);
1739
2117
  const hashBuffer = await globalThis.crypto.subtle.digest('SHA-256', copy);
1740
2118
  return new Uint8Array(hashBuffer);
1741
2119
  }
1742
- return sha256Sync(data);
2120
+ return sha256Pure(data);
2121
+ }
2122
+ /**
2123
+ * Check if a 32-byte value is a valid point on the ed25519 curve.
2124
+ *
2125
+ * A valid PDA must be OFF the curve (it must NOT correspond to a real
2126
+ * ed25519 public key, so that no private key can ever sign for it).
2127
+ *
2128
+ * We determine on-curve status by attempting to decompress the candidate
2129
+ * as a compressed Edwards point. If decompression succeeds the value lies
2130
+ * on the curve; if it throws, the value is off-curve and is a valid PDA.
2131
+ * This matches the behaviour of `PublicKey.isOnCurve` in @solana/web3.js.
2132
+ */
2133
+ function isOnCurve(publicKey) {
2134
+ if (publicKey.length !== 32) {
2135
+ return false;
2136
+ }
2137
+ try {
2138
+ ed25519.Point.fromHex(publicKey);
2139
+ return true;
2140
+ }
2141
+ catch {
2142
+ return false;
2143
+ }
1743
2144
  }
1744
2145
  /**
1745
2146
  * PDA marker bytes appended to seeds before hashing.
@@ -1807,7 +2208,7 @@ async function findProgramAddress(seeds, programId) {
1807
2208
  for (let bump = 255; bump >= 0; bump--) {
1808
2209
  const buffer = buildPdaBuffer(seeds, programIdBytes, bump);
1809
2210
  const hash = await sha256Async(buffer);
1810
- {
2211
+ if (!isOnCurve(hash)) {
1811
2212
  return [encodeBase58(hash), bump];
1812
2213
  }
1813
2214
  }
@@ -1827,7 +2228,7 @@ function findProgramAddressSync(seeds, programId) {
1827
2228
  for (let bump = 255; bump >= 0; bump--) {
1828
2229
  const buffer = buildPdaBuffer(seeds, programIdBytes, bump);
1829
2230
  const hash = sha256Sync(buffer);
1830
- {
2231
+ if (!isOnCurve(hash)) {
1831
2232
  return [encodeBase58(hash), bump];
1832
2233
  }
1833
2234
  }
@@ -1876,6 +2277,134 @@ function createPublicKeySeed(address) {
1876
2277
  return decoded;
1877
2278
  }
1878
2279
 
2280
+ /**
2281
+ * Typed serialization of PDA seed values.
2282
+ *
2283
+ * Shared by the standalone PDA DSL (`pda-dsl.ts`) and instruction account
2284
+ * resolution (`account-resolver.ts`). When a seed carries a declared type
2285
+ * (from the IDL or the `pdas!` registry), encoding is exact: pubkeys are
2286
+ * base58-decoded to 32 bytes, integers are little-endian at the declared
2287
+ * width. Without a type, legacy heuristics apply for backward compatibility.
2288
+ */
2289
+ /**
2290
+ * Normalizes the type-name variants that IDLs and codegen produce
2291
+ * ("Pubkey", "publicKey", "solana_pubkey::Pubkey", "String", ...) to a
2292
+ * canonical seed type. Returns undefined for types that cannot be a seed.
2293
+ */
2294
+ function normalizeSeedType(argType) {
2295
+ if (!argType)
2296
+ return undefined;
2297
+ // Strip any path qualifier (e.g. solana_pubkey::Pubkey).
2298
+ const parts = argType.split('::');
2299
+ const t = (parts[parts.length - 1] ?? argType).trim();
2300
+ if (/^[ui](8|16|32|64|128)$/.test(t)) {
2301
+ return t;
2302
+ }
2303
+ switch (t) {
2304
+ case 'pubkey':
2305
+ case 'Pubkey':
2306
+ case 'publicKey':
2307
+ case 'PublicKey':
2308
+ return 'pubkey';
2309
+ case 'string':
2310
+ case 'String':
2311
+ case 'str':
2312
+ return 'string';
2313
+ default:
2314
+ return undefined;
2315
+ }
2316
+ }
2317
+ /**
2318
+ * Serializes a PDA seed value.
2319
+ *
2320
+ * With a recognized `argType`, encoding is strict and width-exact; an
2321
+ * incompatible value throws rather than deriving a wrong address. Without
2322
+ * one, the legacy heuristics apply: raw bytes pass through, 43/44-character
2323
+ * strings are tried as base58, other strings are utf-8, numbers are 8-byte
2324
+ * little-endian u64.
2325
+ */
2326
+ function serializeSeedValue(value, argType) {
2327
+ if (value instanceof Uint8Array) {
2328
+ return value;
2329
+ }
2330
+ const t = normalizeSeedType(argType);
2331
+ if (t === 'pubkey') {
2332
+ if (typeof value !== 'string') {
2333
+ throw new Error(`Pubkey seed requires a base58 string, got ${typeof value}`);
2334
+ }
2335
+ const decoded = decodeBase58(value);
2336
+ if (decoded.length !== 32) {
2337
+ throw new Error(`Pubkey seed '${value}' decoded to ${decoded.length} bytes, expected 32`);
2338
+ }
2339
+ return decoded;
2340
+ }
2341
+ if (t === 'string') {
2342
+ if (typeof value !== 'string') {
2343
+ throw new Error(`String seed requires a string value, got ${typeof value}`);
2344
+ }
2345
+ return new TextEncoder().encode(value);
2346
+ }
2347
+ if (t !== undefined) {
2348
+ // Numeric type at a declared width.
2349
+ if (typeof value !== 'bigint' && typeof value !== 'number') {
2350
+ throw new Error(`Numeric seed of type ${t} requires a number/bigint, got ${typeof value}`);
2351
+ }
2352
+ const bits = parseInt(t.slice(1), 10);
2353
+ return serializeNumber(value, bits / 8, t.startsWith('i'));
2354
+ }
2355
+ // --- Untyped: legacy heuristics. ---
2356
+ if (typeof value === 'string') {
2357
+ if (value.length === 43 || value.length === 44) {
2358
+ try {
2359
+ return decodeBase58(value);
2360
+ }
2361
+ catch {
2362
+ return new TextEncoder().encode(value);
2363
+ }
2364
+ }
2365
+ return new TextEncoder().encode(value);
2366
+ }
2367
+ if (typeof value === 'bigint' || typeof value === 'number') {
2368
+ return serializeNumber(value, 8, true);
2369
+ }
2370
+ throw new Error(`Cannot serialize value for PDA seed: ${typeof value}`);
2371
+ }
2372
+ /**
2373
+ * Little-endian two's-complement encoding at a fixed byte width, with an
2374
+ * overflow check so out-of-range values fail instead of silently truncating.
2375
+ */
2376
+ function serializeNumber(value, size, signed) {
2377
+ const buffer = new Uint8Array(size);
2378
+ let n = typeof value === 'bigint' ? value : BigInt(value);
2379
+ const original = n;
2380
+ for (let i = 0; i < size; i++) {
2381
+ buffer[i] = Number(n & BigInt(0xff));
2382
+ n >>= BigInt(8);
2383
+ }
2384
+ const fits = signed ? n === BigInt(0) || n === BigInt(-1) : n === BigInt(0);
2385
+ if (!fits) {
2386
+ throw new Error(`Seed value ${original} does not fit in ${size * 8} bits`);
2387
+ }
2388
+ return buffer;
2389
+ }
2390
+
2391
+ function getValueByPath(source, path) {
2392
+ if (!source) {
2393
+ return undefined;
2394
+ }
2395
+ if (Object.prototype.hasOwnProperty.call(source, path)) {
2396
+ return source[path];
2397
+ }
2398
+ let current = source;
2399
+ for (const segment of path.split('.')) {
2400
+ if (current === null || typeof current !== 'object') {
2401
+ return undefined;
2402
+ }
2403
+ current = current[segment];
2404
+ }
2405
+ return current;
2406
+ }
2407
+
1879
2408
  /**
1880
2409
  * Topologically sort accounts so that dependencies (accountRef) are resolved first.
1881
2410
  * Non-PDA accounts come first, then PDAs in dependency order.
@@ -1962,13 +2491,32 @@ function resolveAccounts(accountMetas, args, options) {
1962
2491
  missing.push(meta.name);
1963
2492
  }
1964
2493
  }
1965
- // Return accounts in original order (as defined in accountMetas)
2494
+ // Return accounts in original order (as defined in accountMetas).
2495
+ //
2496
+ // Omitted optional accounts that precede a resolved account cannot simply
2497
+ // be dropped — that would shift every later account into the wrong slot.
2498
+ // Anchor's convention is to pass the program ID as a placeholder; trailing
2499
+ // omitted optionals are dropped as usual.
2500
+ const lastResolvedIndex = accountMetas.reduce((last, meta, index) => (resolvedMap[meta.name] ? index : last), -1);
1966
2501
  const orderedAccounts = [];
1967
- for (const meta of accountMetas) {
2502
+ for (let i = 0; i < accountMetas.length; i++) {
2503
+ const meta = accountMetas[i];
1968
2504
  const resolved = resolvedMap[meta.name];
1969
2505
  if (resolved) {
1970
2506
  orderedAccounts.push(resolved);
1971
2507
  }
2508
+ else if (meta.isOptional && i < lastResolvedIndex) {
2509
+ if (!options.programId) {
2510
+ throw new Error('Omitted optional account "' + meta.name + '" precedes other accounts and needs ' +
2511
+ 'the program ID as a placeholder, but no programId was provided in options.');
2512
+ }
2513
+ orderedAccounts.push({
2514
+ name: meta.name,
2515
+ address: options.programId,
2516
+ isSigner: false,
2517
+ isWritable: false,
2518
+ });
2519
+ }
1972
2520
  }
1973
2521
  return {
1974
2522
  accounts: orderedAccounts,
@@ -1978,24 +2526,25 @@ function resolveAccounts(accountMetas, args, options) {
1978
2526
  function resolveSingleAccount(meta, args, options, resolvedMap) {
1979
2527
  switch (meta.category) {
1980
2528
  case 'signer':
1981
- return resolveSignerAccount(meta, options.wallet);
2529
+ return resolveSignerAccount(meta, options.accounts, options.wallet);
1982
2530
  case 'known':
1983
2531
  return resolveKnownAccount(meta);
1984
2532
  case 'pda':
1985
- return resolvePdaAccount(meta, args, resolvedMap, options.programId);
2533
+ return resolvePdaAccount(meta, args, resolvedMap, options.programId, options.resolve);
1986
2534
  case 'userProvided':
1987
2535
  return resolveUserProvidedAccount(meta, options.accounts);
1988
2536
  default:
1989
2537
  return null;
1990
2538
  }
1991
2539
  }
1992
- function resolveSignerAccount(meta, wallet) {
1993
- if (!wallet) {
2540
+ function resolveSignerAccount(meta, accounts, wallet) {
2541
+ const address = accounts?.[meta.name] ?? wallet?.publicKey;
2542
+ if (!address) {
1994
2543
  return null;
1995
2544
  }
1996
2545
  return {
1997
2546
  name: meta.name,
1998
- address: wallet.publicKey,
2547
+ address,
1999
2548
  isSigner: true,
2000
2549
  isWritable: meta.isWritable,
2001
2550
  };
@@ -2011,7 +2560,7 @@ function resolveKnownAccount(meta) {
2011
2560
  isWritable: meta.isWritable,
2012
2561
  };
2013
2562
  }
2014
- function resolvePdaAccount(meta, args, resolvedMap, programId) {
2563
+ function resolvePdaAccount(meta, args, resolvedMap, programId, resolve) {
2015
2564
  if (!meta.pdaConfig) {
2016
2565
  return null;
2017
2566
  }
@@ -2028,13 +2577,16 @@ function resolvePdaAccount(meta, args, resolvedMap, programId) {
2028
2577
  case 'literal':
2029
2578
  seeds.push(createSeed(seed.value));
2030
2579
  break;
2580
+ case 'bytes':
2581
+ seeds.push(Uint8Array.from(seed.value));
2582
+ break;
2031
2583
  case 'argRef': {
2032
- const argValue = args[seed.argName];
2584
+ const argValue = getValueByPath(args, seed.argName) ?? getValueByPath(resolve, seed.argName);
2033
2585
  if (argValue === undefined) {
2034
2586
  throw new Error('PDA seed references missing argument: ' + seed.argName +
2035
2587
  ' (for account "' + meta.name + '")');
2036
2588
  }
2037
- seeds.push(createSeed(argValue));
2589
+ seeds.push(serializeSeedValue(argValue, seed.argType));
2038
2590
  break;
2039
2591
  }
2040
2592
  case 'accountRef': {
@@ -2102,6 +2654,11 @@ function serializeInstructionData(discriminator, args, schema) {
2102
2654
  const buffers = [Buffer.from(discriminator)];
2103
2655
  for (const field of schema) {
2104
2656
  const value = args[field.name];
2657
+ // Option fields treat undefined as None; everything else must be present
2658
+ // (silently encoding zeros for a missing arg corrupts instruction data).
2659
+ if (value === undefined && !(typeof field.type === 'object' && 'option' in field.type)) {
2660
+ throw new Error(`Missing required argument "${field.name}" (type ${JSON.stringify(field.type)})`);
2661
+ }
2105
2662
  const serialized = serializeValue(value, field.type);
2106
2663
  buffers.push(serialized);
2107
2664
  }
@@ -2120,8 +2677,108 @@ function serializeValue(value, type) {
2120
2677
  if ('array' in type) {
2121
2678
  return serializeArray(value, type.array[0], type.array[1]);
2122
2679
  }
2680
+ if ('hashMap' in type) {
2681
+ return serializeHashMap(value, type.hashMap[0], type.hashMap[1]);
2682
+ }
2683
+ if ('struct' in type) {
2684
+ return serializeStruct(value, type.struct);
2685
+ }
2686
+ if ('enum' in type) {
2687
+ return serializeEnum(value, type.enum);
2688
+ }
2123
2689
  throw new Error(`Unknown type: ${JSON.stringify(type)}`);
2124
2690
  }
2691
+ function serializeStruct(value, fields) {
2692
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
2693
+ throw new Error(`Struct value must be a plain object, got ${typeof value}`);
2694
+ }
2695
+ const obj = value;
2696
+ const buffers = [];
2697
+ for (const field of fields) {
2698
+ const fieldValue = obj[field.name];
2699
+ if (fieldValue === undefined &&
2700
+ !(typeof field.type === 'object' && 'option' in field.type)) {
2701
+ throw new Error(`Missing required struct field "${field.name}"`);
2702
+ }
2703
+ buffers.push(serializeValue(fieldValue, field.type));
2704
+ }
2705
+ return Buffer.concat(buffers);
2706
+ }
2707
+ function serializeHashMap(value, keyType, valueType) {
2708
+ if (keyType !== 'string') {
2709
+ throw new Error(`Instruction hashMap keys must use the 'string' schema, got ${JSON.stringify(keyType)}`);
2710
+ }
2711
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
2712
+ throw new Error(`HashMap value must be a plain object, got ${typeof value}`);
2713
+ }
2714
+ const entries = Object.entries(value).sort(([left], [right]) =>
2715
+ // Rust/Borsh sorts String keys by their UTF-8 bytes before serializing.
2716
+ Buffer.compare(Buffer.from(left, 'utf8'), Buffer.from(right, 'utf8')));
2717
+ const len = Buffer.alloc(4);
2718
+ len.writeUInt32LE(entries.length, 0);
2719
+ const entryBuffers = [];
2720
+ for (const [key, entryValue] of entries) {
2721
+ entryBuffers.push(serializePrimitive(key, 'string'));
2722
+ entryBuffers.push(serializeValue(entryValue, valueType));
2723
+ }
2724
+ return Buffer.concat([len, ...entryBuffers]);
2725
+ }
2726
+ function variantName(variant) {
2727
+ return typeof variant === 'string' ? variant : variant.name;
2728
+ }
2729
+ function serializeEnum(value, variants) {
2730
+ // Numeric value: a bare variant index (fieldless variants only).
2731
+ if (typeof value === 'number') {
2732
+ if (!Number.isInteger(value) || value < 0 || value >= variants.length) {
2733
+ throw new Error(`Enum variant index ${value} out of range (0..${variants.length - 1})`);
2734
+ }
2735
+ const variant = variants[value];
2736
+ if (typeof variant !== 'string') {
2737
+ throw new Error(`Enum variant "${variantName(variant)}" carries data; pass { ${variantName(variant)}: ... } instead of an index`);
2738
+ }
2739
+ return Buffer.from([value]);
2740
+ }
2741
+ // String value: a fieldless variant by name.
2742
+ if (typeof value === 'string') {
2743
+ const index = variants.findIndex((v) => variantName(v) === value);
2744
+ if (index === -1) {
2745
+ throw new Error(`Unknown enum variant "${value}". Expected one of: ${variants.map(variantName).join(', ')}`);
2746
+ }
2747
+ const variant = variants[index];
2748
+ if (typeof variant !== 'string') {
2749
+ throw new Error(`Enum variant "${value}" carries data; pass { ${value}: ... } instead of a bare name`);
2750
+ }
2751
+ return Buffer.from([index]);
2752
+ }
2753
+ // Object value: { variantName: payload } for data-carrying variants.
2754
+ if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
2755
+ const keys = Object.keys(value);
2756
+ if (keys.length !== 1) {
2757
+ throw new Error(`Enum value must be a single-key object ({ variantName: payload }), got keys [${keys.join(', ')}]`);
2758
+ }
2759
+ const key = keys[0];
2760
+ const payload = value[key];
2761
+ const index = variants.findIndex((v) => variantName(v) === key);
2762
+ if (index === -1) {
2763
+ throw new Error(`Unknown enum variant "${key}". Expected one of: ${variants.map(variantName).join(', ')}`);
2764
+ }
2765
+ const variant = variants[index];
2766
+ if (typeof variant === 'string') {
2767
+ throw new Error(`Enum variant "${key}" is fieldless; pass '${key}' instead of an object`);
2768
+ }
2769
+ const prefix = Buffer.from([index]);
2770
+ if ('fields' in variant) {
2771
+ return Buffer.concat([prefix, serializeStruct(payload, variant.fields)]);
2772
+ }
2773
+ const tuple = payload;
2774
+ if (!Array.isArray(tuple) || tuple.length !== variant.tuple.length) {
2775
+ throw new Error(`Enum variant "${key}" expects a tuple of length ${variant.tuple.length}`);
2776
+ }
2777
+ const elements = variant.tuple.map((elementType, i) => serializeValue(tuple[i], elementType));
2778
+ return Buffer.concat([prefix, ...elements]);
2779
+ }
2780
+ throw new Error(`Cannot serialize enum from value of type ${typeof value}`);
2781
+ }
2125
2782
  function serializePrimitive(value, type) {
2126
2783
  switch (type) {
2127
2784
  case 'u8':
@@ -2162,21 +2819,60 @@ function serializePrimitive(value, type) {
2162
2819
  case 'i128':
2163
2820
  const i128 = Buffer.alloc(16);
2164
2821
  const bigI128 = BigInt(value);
2165
- i128.writeBigInt64LE(bigI128 & BigInt('0xFFFFFFFFFFFFFFFF'), 0);
2822
+ // The masked low limb is always non-negative, so it must be written
2823
+ // unsigned; the arithmetic-shifted high limb carries the sign.
2824
+ i128.writeBigUInt64LE(bigI128 & BigInt('0xFFFFFFFFFFFFFFFF'), 0);
2166
2825
  i128.writeBigInt64LE(bigI128 >> BigInt(64), 8);
2167
2826
  return i128;
2827
+ case 'f32': {
2828
+ const f32 = Buffer.alloc(4);
2829
+ f32.writeFloatLE(value, 0);
2830
+ return f32;
2831
+ }
2832
+ case 'f64': {
2833
+ const f64 = Buffer.alloc(8);
2834
+ f64.writeDoubleLE(value, 0);
2835
+ return f64;
2836
+ }
2168
2837
  case 'bool':
2169
2838
  return Buffer.from([value ? 1 : 0]);
2839
+ case 'bytes': {
2840
+ // Borsh bytes: u32 LE length prefix + raw bytes.
2841
+ const raw = value instanceof Uint8Array
2842
+ ? value
2843
+ : Array.isArray(value)
2844
+ ? Uint8Array.from(value)
2845
+ : null;
2846
+ if (raw === null) {
2847
+ throw new Error(`Cannot serialize bytes from value of type ${typeof value}`);
2848
+ }
2849
+ const lenPrefix = Buffer.alloc(4);
2850
+ lenPrefix.writeUInt32LE(raw.length, 0);
2851
+ return Buffer.concat([lenPrefix, Buffer.from(raw)]);
2852
+ }
2170
2853
  case 'string':
2171
2854
  const str = value;
2172
2855
  const strBytes = Buffer.from(str, 'utf-8');
2173
2856
  const strLen = Buffer.alloc(4);
2174
2857
  strLen.writeUInt32LE(strBytes.length, 0);
2175
2858
  return Buffer.concat([strLen, strBytes]);
2176
- case 'pubkey':
2177
- // Public key is 32 bytes
2178
- // In production, decode base58 to 32 bytes
2179
- return Buffer.alloc(32, 0);
2859
+ case 'pubkey': {
2860
+ // Public key is 32 bytes. Accept base58 strings or raw 32-byte buffers.
2861
+ if (value instanceof Uint8Array) {
2862
+ if (value.length !== 32) {
2863
+ throw new Error(`Invalid pubkey byte length: expected 32, got ${value.length}`);
2864
+ }
2865
+ return Buffer.from(value);
2866
+ }
2867
+ if (typeof value === 'string') {
2868
+ const decoded = decodeBase58(value);
2869
+ if (decoded.length !== 32) {
2870
+ throw new Error(`Invalid pubkey: '${value}' decoded to ${decoded.length} bytes, expected 32`);
2871
+ }
2872
+ return Buffer.from(decoded);
2873
+ }
2874
+ throw new Error(`Cannot serialize pubkey from value of type ${typeof value}`);
2875
+ }
2180
2876
  default:
2181
2877
  throw new Error(`Unknown primitive type: ${type}`);
2182
2878
  }
@@ -2203,59 +2899,7 @@ function serializeArray(values, elementType, length) {
2203
2899
  }
2204
2900
 
2205
2901
  /**
2206
- * Waits for transaction confirmation.
2207
- *
2208
- * @param signature - Transaction signature
2209
- * @param level - Desired confirmation level
2210
- * @param timeout - Maximum wait time in milliseconds
2211
- * @returns Confirmation result
2212
- */
2213
- async function waitForConfirmation(signature, level = 'confirmed', timeout = 60000) {
2214
- const startTime = Date.now();
2215
- while (Date.now() - startTime < timeout) {
2216
- const status = await checkTransactionStatus();
2217
- if (status.err) {
2218
- throw new Error(`Transaction failed: ${JSON.stringify(status.err)}`);
2219
- }
2220
- if (isConfirmationLevelSufficient(status.confirmations, level)) {
2221
- return {
2222
- level,
2223
- slot: status.slot,
2224
- };
2225
- }
2226
- await sleep(1000);
2227
- }
2228
- throw new Error(`Transaction confirmation timeout after ${timeout}ms`);
2229
- }
2230
- async function checkTransactionStatus(_signature) {
2231
- // In production, query the Solana RPC
2232
- return {
2233
- err: null,
2234
- confirmations: 32,
2235
- slot: 123456789,
2236
- };
2237
- }
2238
- function isConfirmationLevelSufficient(confirmations, level) {
2239
- if (confirmations === null) {
2240
- return false;
2241
- }
2242
- switch (level) {
2243
- case 'processed':
2244
- return confirmations >= 0;
2245
- case 'confirmed':
2246
- return confirmations >= 1;
2247
- case 'finalized':
2248
- return confirmations >= 32;
2249
- default:
2250
- return false;
2251
- }
2252
- }
2253
- function sleep(ms) {
2254
- return new Promise(resolve => setTimeout(resolve, ms));
2255
- }
2256
-
2257
- /**
2258
- * Parses and handles instruction errors.
2902
+ * Parses and handles instruction errors.
2259
2903
  */
2260
2904
  /**
2261
2905
  * Parses an error returned from a Solana transaction.
@@ -2313,91 +2957,159 @@ function extractErrorCode(error) {
2313
2957
  function formatProgramError(error) {
2314
2958
  return `${error.name} (${error.code}): ${error.message}`;
2315
2959
  }
2316
-
2317
2960
  /**
2318
- * Converts resolved account array to a map for the builder.
2961
+ * Error thrown when an instruction fails to send and the underlying failure
2962
+ * could be parsed against the handler's IDL error definitions.
2319
2963
  */
2320
- function toResolvedAccountsMap(accounts) {
2321
- const map = {};
2322
- for (const account of accounts) {
2323
- map[account.name] = account.address;
2964
+ class InstructionError extends Error {
2965
+ constructor(message, programError, cause) {
2966
+ super(message);
2967
+ this.name = 'InstructionError';
2968
+ this.programError = programError;
2969
+ this.cause = cause;
2324
2970
  }
2325
- return map;
2971
+ }
2972
+
2973
+ /**
2974
+ * Creates a data-driven instruction handler.
2975
+ *
2976
+ * The returned handler implements `build()` generically: it serializes args
2977
+ * via the schema-driven serializer and constructs the account key list from
2978
+ * the resolved, ordered accounts. No imperative per-instruction code is
2979
+ * required, which keeps generated SDKs tiny and puts all serialization logic
2980
+ * in one tested place.
2981
+ */
2982
+ function createInstructionHandler(config) {
2983
+ const discriminator = config.discriminator instanceof Uint8Array
2984
+ ? config.discriminator
2985
+ : Uint8Array.from(config.discriminator);
2986
+ const argNames = config.args.map((a) => a.name);
2987
+ const errors = config.errors ?? [];
2988
+ return {
2989
+ programId: config.programId,
2990
+ accounts: config.accounts,
2991
+ errors,
2992
+ argNames,
2993
+ build(args, resolved) {
2994
+ const data = serializeInstructionData(discriminator, args, config.args);
2995
+ return {
2996
+ programId: config.programId,
2997
+ keys: resolved.map((r) => ({
2998
+ pubkey: r.address,
2999
+ isSigner: r.isSigner,
3000
+ isWritable: r.isWritable,
3001
+ })),
3002
+ data,
3003
+ };
3004
+ },
3005
+ };
2326
3006
  }
2327
3007
  /**
2328
- * Executes an instruction handler with the given arguments and options.
3008
+ * Splits a merged params object into serialized args and account overrides.
2329
3009
  *
2330
- * This is the main function for executing Solana instructions. It handles:
2331
- * 1. Account resolution (signer, PDA, user-provided)
2332
- * 2. Calling the generated build() function
2333
- * 3. Transaction signing and sending
2334
- * 4. Confirmation waiting
3010
+ * Keys matching a declared argument name are args; keys matching a declared
3011
+ * account name (with a string value) are account address overrides. This
3012
+ * applies to signer slots too, allowing explicit signer addresses to override
3013
+ * the wallet fallback. Anything else throws — a typo'd key silently dropped
3014
+ * here would otherwise change the built instruction. `options.accounts` on
3015
+ * {@link BuildOptions} remains an unvalidated escape hatch for advanced callers.
3016
+ */
3017
+ function splitParams(handler, params) {
3018
+ const argNameSet = new Set(handler.argNames);
3019
+ const accountNameSet = new Set(handler.accounts.map((a) => a.name));
3020
+ const args = {};
3021
+ const accountOverrides = {};
3022
+ let resolve;
3023
+ for (const [key, value] of Object.entries(params)) {
3024
+ if (argNameSet.has(key)) {
3025
+ args[key] = value;
3026
+ }
3027
+ else if (key === 'resolve' && !accountNameSet.has(key)) {
3028
+ if (value === undefined) {
3029
+ continue;
3030
+ }
3031
+ if (value === null || Array.isArray(value) || typeof value !== 'object') {
3032
+ throw new Error('Parameter "resolve" must be an object when provided');
3033
+ }
3034
+ resolve = value;
3035
+ }
3036
+ else if (accountNameSet.has(key)) {
3037
+ if (typeof value !== 'string') {
3038
+ // Non-string values are not valid account addresses.
3039
+ throw new Error(`Parameter "${key}" is not a known argument and is not a base58 account address`);
3040
+ }
3041
+ accountOverrides[key] = value;
3042
+ }
3043
+ else {
3044
+ throw new Error(`Unknown parameter "${key}". Expected one of args [${[...argNameSet].join(', ')}] ` +
3045
+ `or accounts [${[...accountNameSet].join(', ')}]`);
3046
+ }
3047
+ }
3048
+ return { args, accountOverrides, resolve };
3049
+ }
3050
+ /**
3051
+ * Builds a {@link BuiltInstruction} from a handler and a merged params object.
2335
3052
  *
2336
- * @param handler - Instruction handler from generated SDK
2337
- * @param args - Instruction arguments
2338
- * @param options - Execution options
2339
- * @returns Execution result with signature
3053
+ * This is a pure function: it performs no network access. It is the unit of
3054
+ * composition for batching (`wallet.signAndSend([a, b, c])`).
2340
3055
  */
2341
- async function executeInstruction(handler, args, options = {}) {
2342
- // Step 1: Resolve accounts using handler's account metadata
3056
+ function buildInstruction(handler, params, options = {}) {
3057
+ const { args, accountOverrides, resolve } = splitParams(handler, params);
2343
3058
  const resolutionOptions = {
2344
- accounts: options.accounts,
3059
+ accounts: { ...accountOverrides, ...options.accounts },
3060
+ resolve,
2345
3061
  wallet: options.wallet,
2346
- programId: handler.programId, // Pass programId for PDA derivation
3062
+ programId: handler.programId,
2347
3063
  };
2348
3064
  const resolution = resolveAccounts(handler.accounts, args, resolutionOptions);
2349
3065
  validateAccountResolution(resolution);
2350
- // Step 2: Call generated build() function
2351
- const resolvedAccountsMap = toResolvedAccountsMap(resolution.accounts);
2352
- const instruction = handler.build(args, resolvedAccountsMap);
2353
- // Step 3: Build transaction from the built instruction
2354
- const transaction = buildTransaction(instruction);
2355
- // Step 4: Sign and send
2356
- if (!options.wallet) {
2357
- throw new Error('Wallet required to sign transaction');
3066
+ const instruction = handler.build(args, resolution.accounts);
3067
+ if (options.remainingAccounts?.length) {
3068
+ instruction.keys.push(...options.remainingAccounts);
2358
3069
  }
2359
- const signature = await options.wallet.signAndSend(transaction);
2360
- // Step 5: Wait for confirmation
2361
- const confirmationLevel = options.confirmationLevel ?? 'confirmed';
2362
- const timeout = options.timeout ?? 60000;
2363
- const confirmation = await waitForConfirmation(signature, confirmationLevel, timeout);
2364
- return {
2365
- signature,
2366
- confirmationLevel: confirmation.level,
2367
- slot: confirmation.slot,
2368
- };
3070
+ return instruction;
2369
3071
  }
2370
3072
  /**
2371
- * Creates a transaction object from a built instruction.
3073
+ * Builds, signs, and sends an instruction via the wallet adapter.
2372
3074
  *
2373
- * @param instruction - Built instruction from handler
2374
- * @returns Transaction object ready for signing
3075
+ * The core SDK does not touch RPC: the adapter owns blockhash, compilation,
3076
+ * signing, sending, and confirmation. On failure, program errors are parsed
3077
+ * against the handler's IDL error definitions and surfaced as an
3078
+ * {@link InstructionError}.
2375
3079
  */
2376
- function buildTransaction(instruction) {
2377
- // This returns a framework-agnostic transaction representation.
2378
- // The wallet adapter is responsible for converting this to the
2379
- // appropriate format (@solana/web3.js Transaction, etc.)
2380
- return {
2381
- instructions: [{
2382
- programId: instruction.programId,
2383
- keys: instruction.keys,
2384
- data: Array.from(instruction.data),
2385
- }],
3080
+ async function executeInstruction(handler, params, options = {}) {
3081
+ const instruction = buildInstruction(handler, params, options);
3082
+ if (!options.wallet) {
3083
+ throw new Error('Wallet required to sign and send transaction');
3084
+ }
3085
+ const sendOptions = {
3086
+ ...options.send,
2386
3087
  };
3088
+ if (options.confirmationLevel !== undefined) {
3089
+ sendOptions.confirmationLevel = options.confirmationLevel;
3090
+ }
3091
+ try {
3092
+ const result = await options.wallet.signAndSend([instruction], sendOptions);
3093
+ return { signature: result.signature, slot: result.slot };
3094
+ }
3095
+ catch (err) {
3096
+ const programError = parseInstructionError(err, handler.errors);
3097
+ if (programError) {
3098
+ throw new InstructionError(`${programError.name} (${programError.code}): ${programError.message}`, programError, err);
3099
+ }
3100
+ throw err;
3101
+ }
2387
3102
  }
2388
3103
  /**
2389
3104
  * Creates an instruction executor bound to a specific wallet.
2390
- *
2391
- * @param wallet - Wallet adapter
2392
- * @returns Bound executor function
2393
3105
  */
2394
3106
  function createInstructionExecutor(wallet) {
2395
3107
  return {
2396
- execute: async (handler, args, options) => {
2397
- return executeInstruction(handler, args, {
2398
- ...options,
2399
- wallet,
2400
- });
3108
+ execute: async (handler, params, options) => {
3109
+ return executeInstruction(handler, params, { ...options, wallet });
3110
+ },
3111
+ build: (handler, params, options) => {
3112
+ return buildInstruction(handler, params, { ...options, wallet });
2401
3113
  },
2402
3114
  };
2403
3115
  }
@@ -2422,11 +3134,11 @@ function resolveSeeds(seeds, context) {
2422
3134
  case 'bytes':
2423
3135
  return seed.value;
2424
3136
  case 'argRef': {
2425
- const value = context.args?.[seed.argName];
3137
+ const value = getValueByPath(context.args, seed.argName) ?? getValueByPath(context.resolve, seed.argName);
2426
3138
  if (value === undefined) {
2427
3139
  throw new Error(`Missing arg for PDA seed: ${seed.argName}`);
2428
3140
  }
2429
- return serializeArgForSeed(value, seed.argType);
3141
+ return serializeSeedValue(value, seed.argType);
2430
3142
  }
2431
3143
  case 'accountRef': {
2432
3144
  const address = context.accounts?.[seed.accountName];
@@ -2438,47 +3150,6 @@ function resolveSeeds(seeds, context) {
2438
3150
  }
2439
3151
  });
2440
3152
  }
2441
- function serializeArgForSeed(value, argType) {
2442
- if (value instanceof Uint8Array) {
2443
- return value;
2444
- }
2445
- if (typeof value === 'string') {
2446
- if (value.length === 43 || value.length === 44) {
2447
- try {
2448
- return decodeBase58(value);
2449
- }
2450
- catch {
2451
- return new TextEncoder().encode(value);
2452
- }
2453
- }
2454
- return new TextEncoder().encode(value);
2455
- }
2456
- if (typeof value === 'bigint' || typeof value === 'number') {
2457
- const size = getArgSize(argType);
2458
- return serializeNumber(value, size);
2459
- }
2460
- throw new Error(`Cannot serialize value for PDA seed: ${typeof value}`);
2461
- }
2462
- function getArgSize(argType) {
2463
- if (!argType)
2464
- return 8;
2465
- const match = argType.match(/^[ui](\d+)$/);
2466
- if (match && match[1]) {
2467
- return parseInt(match[1], 10) / 8;
2468
- }
2469
- if (argType === 'pubkey')
2470
- return 32;
2471
- return 8;
2472
- }
2473
- function serializeNumber(value, size) {
2474
- const buffer = new Uint8Array(size);
2475
- let n = typeof value === 'bigint' ? value : BigInt(value);
2476
- for (let i = 0; i < size; i++) {
2477
- buffer[i] = Number(n & BigInt(0xff));
2478
- n >>= BigInt(8);
2479
- }
2480
- return buffer;
2481
- }
2482
3153
  function pda(programId, ...seeds) {
2483
3154
  return {
2484
3155
  seeds,
@@ -2504,560 +3175,1250 @@ function createProgramPdas(pdas) {
2504
3175
  return pdas;
2505
3176
  }
2506
3177
 
2507
- class Arete {
2508
- constructor(url, options) {
2509
- this.stack = options.stack;
2510
- this.storage = new SortedStorageDecorator(options.storage ?? new MemoryAdapter());
2511
- this.processor = new FrameProcessor(this.storage, {
2512
- maxEntriesPerView: options.maxEntriesPerView,
2513
- flushIntervalMs: options.flushIntervalMs,
2514
- schemas: options.validateFrames ? this.stack.schemas : undefined,
2515
- });
2516
- this.connection = new ConnectionManager({
2517
- websocketUrl: url,
2518
- reconnectIntervals: options.reconnectIntervals,
2519
- maxReconnectAttempts: options.maxReconnectAttempts,
2520
- auth: options.auth,
2521
- });
2522
- this.subscriptionRegistry = new SubscriptionRegistry(this.connection);
2523
- this.connection.onFrame((frame) => {
2524
- this.processor.handleFrame(frame);
2525
- });
2526
- this._views = createTypedViews(this.stack, this.storage, this.subscriptionRegistry);
2527
- this._instructions = this.buildInstructions();
2528
- }
2529
- buildInstructions() {
2530
- const instructions = {};
2531
- if (this.stack.instructions) {
2532
- for (const [name, handler] of Object.entries(this.stack.instructions)) {
2533
- instructions[name] = (args, options) => {
2534
- return executeInstruction(handler, args, options);
2535
- };
2536
- }
2537
- }
2538
- return instructions;
2539
- }
2540
- static async connect(stack, options) {
2541
- const url = options?.url ?? stack.url;
2542
- if (!url) {
2543
- throw new AreteError('URL is required (provide url option or define url in stack)', 'INVALID_CONFIG');
2544
- }
2545
- const internalOptions = {
2546
- stack,
2547
- storage: options?.storage,
2548
- maxEntriesPerView: options?.maxEntriesPerView,
2549
- flushIntervalMs: options?.flushIntervalMs,
2550
- autoReconnect: options?.autoReconnect,
2551
- reconnectIntervals: options?.reconnectIntervals,
2552
- maxReconnectAttempts: options?.maxReconnectAttempts,
2553
- validateFrames: options?.validateFrames,
2554
- auth: options?.auth,
2555
- };
2556
- const client = new Arete(url, internalOptions);
2557
- if (options?.autoReconnect !== false) {
2558
- await client.connection.connect();
3178
+ const STACK_RUNTIME_EXTENSIONS = '__areteStackRuntimeExtensions';
3179
+ const PROGRAM_OPERATION_EXTENSIONS = '__areteProgramOperationExtensions';
3180
+ function defineProgramExtensions() {
3181
+ return (extensions) => extensions;
3182
+ }
3183
+ function mergeNamespace(base, extension) {
3184
+ if (base && typeof base === 'object' && !Array.isArray(base)
3185
+ && extension && typeof extension === 'object' && !Array.isArray(extension)) {
3186
+ const merged = { ...base };
3187
+ for (const [key, value] of Object.entries(extension)) {
3188
+ merged[key] = key in merged ? mergeNamespace(merged[key], value) : value;
2559
3189
  }
2560
- return client;
3190
+ return merged;
2561
3191
  }
2562
- get views() {
2563
- return this._views;
3192
+ return extension;
3193
+ }
3194
+ function mergeOperations(base, extension) {
3195
+ return {
3196
+ instructions: mergeNamespace(base?.instructions, extension?.instructions),
3197
+ transactions: mergeNamespace(base?.transactions, extension?.transactions),
3198
+ flows: mergeNamespace(base?.flows, extension?.flows),
3199
+ };
3200
+ }
3201
+ function extendProgram(program, extensions) {
3202
+ const base = program;
3203
+ const extended = { ...program };
3204
+ for (const key of ['pdas', 'accounts', 'queries', 'addresses', 'constants', 'defaults', 'math']) {
3205
+ const extensionValue = extensions[key];
3206
+ if (extensionValue !== undefined) {
3207
+ extended[key] = mergeNamespace(base[key], extensionValue);
3208
+ }
3209
+ }
3210
+ if (extensions.raw !== undefined) {
3211
+ extended.rawInstructions = mergeNamespace(base.rawInstructions, extensions.raw);
3212
+ }
3213
+ const baseFactory = base[PROGRAM_OPERATION_EXTENSIONS]?.createOperations;
3214
+ const extensionFactory = extensions.createOperations;
3215
+ if (baseFactory || extensionFactory) {
3216
+ Object.defineProperty(extended, PROGRAM_OPERATION_EXTENSIONS, {
3217
+ value: {
3218
+ createOperations(context) {
3219
+ const baseOperations = baseFactory?.(context);
3220
+ if (baseOperations) {
3221
+ const connectedProgram = context.program;
3222
+ connectedProgram.instructions = mergeNamespace(connectedProgram.instructions, baseOperations.instructions);
3223
+ connectedProgram.transactions = mergeNamespace(connectedProgram.transactions, baseOperations.transactions);
3224
+ connectedProgram.flows = mergeNamespace(connectedProgram.flows, baseOperations.flows);
3225
+ }
3226
+ return mergeOperations(baseOperations, extensionFactory?.(context));
3227
+ },
3228
+ },
3229
+ enumerable: false,
3230
+ configurable: false,
3231
+ writable: false,
3232
+ });
2564
3233
  }
2565
- get instructions() {
2566
- return this._instructions;
3234
+ return extended;
3235
+ }
3236
+ function extendPrograms(programs, extensions) {
3237
+ const merged = { ...programs };
3238
+ for (const [name, program] of Object.entries(programs)) {
3239
+ const extension = extensions[name];
3240
+ merged[name] = extension
3241
+ ? extendProgram(program, extension)
3242
+ : program;
3243
+ }
3244
+ return merged;
3245
+ }
3246
+ function getProgramRuntimeExtensions(program) {
3247
+ return program[PROGRAM_OPERATION_EXTENSIONS];
3248
+ }
3249
+ function defineStackExtensions() {
3250
+ return (extensions) => extensions;
3251
+ }
3252
+ function extendStack(stack, extensions) {
3253
+ const base = stack;
3254
+ const extended = { ...stack };
3255
+ for (const key of ['addresses', 'constants', 'defaults', 'math']) {
3256
+ if (extensions[key] !== undefined) {
3257
+ extended[key] = mergeNamespace(base[key], extensions[key]);
3258
+ }
3259
+ }
3260
+ const baseRuntime = base[STACK_RUNTIME_EXTENSIONS];
3261
+ if (baseRuntime || extensions.createRead || extensions.createFlows) {
3262
+ Object.defineProperty(extended, STACK_RUNTIME_EXTENSIONS, {
3263
+ value: {
3264
+ createRead: baseRuntime?.createRead && extensions.createRead
3265
+ ? (client) => mergeNamespace(baseRuntime.createRead(client), extensions.createRead(client))
3266
+ : extensions.createRead ?? baseRuntime?.createRead,
3267
+ createFlows: baseRuntime?.createFlows && extensions.createFlows
3268
+ ? (client) => mergeNamespace(baseRuntime.createFlows(client), extensions.createFlows(client))
3269
+ : extensions.createFlows ?? baseRuntime?.createFlows,
3270
+ },
3271
+ enumerable: false,
3272
+ configurable: false,
3273
+ writable: false,
3274
+ });
2567
3275
  }
2568
- get connectionState() {
2569
- return this.connection.getState();
3276
+ return extended;
3277
+ }
3278
+ function getStackRuntimeExtensions(stack) {
3279
+ return stack[STACK_RUNTIME_EXTENSIONS];
3280
+ }
3281
+ function defineClientField(target, key, value) {
3282
+ if (value === undefined || key in target) {
3283
+ return;
3284
+ }
3285
+ Object.defineProperty(target, key, {
3286
+ value,
3287
+ enumerable: true,
3288
+ configurable: true,
3289
+ writable: false,
3290
+ });
3291
+ }
3292
+ function applyConnectedStackExtensions(client, stack) {
3293
+ const extendedStack = stack;
3294
+ defineClientField(client, 'addresses', extendedStack.addresses);
3295
+ defineClientField(client, 'constants', extendedStack.constants);
3296
+ defineClientField(client, 'defaults', extendedStack.defaults);
3297
+ defineClientField(client, 'math', extendedStack.math);
3298
+ const runtime = getStackRuntimeExtensions(stack);
3299
+ defineClientField(client, 'flows', runtime?.createFlows?.(client));
3300
+ defineClientField(client, 'read', runtime?.createRead?.(client));
3301
+ return client;
3302
+ }
3303
+
3304
+ function isPreparedInstruction(instruction) {
3305
+ return 'kind' in instruction
3306
+ && instruction.kind === 'instruction'
3307
+ && 'instruction' in instruction;
3308
+ }
3309
+ function nonEmpty(values, label) {
3310
+ if (values.length === 0) {
3311
+ throw new Error(`${label} must contain at least one item`);
2570
3312
  }
2571
- get stackName() {
2572
- return this.stack.name;
3313
+ return [...values];
3314
+ }
3315
+ function dedupe(values) {
3316
+ return [...new Set(values)];
3317
+ }
3318
+ function inferSignerAddresses(instructions) {
3319
+ return dedupe(instructions.flatMap((instruction) => instruction.keys.filter((key) => key.isSigner).map((key) => key.pubkey)));
3320
+ }
3321
+ function createPreparedTransactionBody(input) {
3322
+ const instructions = nonEmpty(input.instructions, `Transaction '${input.name}'`);
3323
+ return {
3324
+ name: input.name,
3325
+ instructions,
3326
+ requiredSignerAddresses: dedupe(input.requiredSignerAddresses ?? inferSignerAddresses(instructions)),
3327
+ errors: [...(input.errors ?? [])],
3328
+ };
3329
+ }
3330
+ function createPlan(name, artifacts, transactions) {
3331
+ return {
3332
+ name,
3333
+ artifacts,
3334
+ transactions: nonEmpty(transactions, `Flow '${name}'`),
3335
+ };
3336
+ }
3337
+ function createPreparedInstruction(input) {
3338
+ const transaction = createPreparedTransactionBody({
3339
+ name: input.name,
3340
+ instructions: [input.instruction],
3341
+ requiredSignerAddresses: input.requiredSignerAddresses,
3342
+ errors: input.errors,
3343
+ });
3344
+ return {
3345
+ kind: 'instruction',
3346
+ name: input.name,
3347
+ instruction: input.instruction,
3348
+ transaction,
3349
+ plan: createPlan(input.name, input.artifacts, [transaction]),
3350
+ artifacts: input.artifacts,
3351
+ };
3352
+ }
3353
+ function createPreparedTransaction(input) {
3354
+ const hasInstructions = input.instructions !== undefined;
3355
+ const hasOperations = input.operations !== undefined;
3356
+ if (hasInstructions === hasOperations) {
3357
+ throw new Error(`Transaction '${input.name}' must provide exactly one of instructions or operations`);
3358
+ }
3359
+ const transactionParts = hasOperations
3360
+ ? input.operations.map((operation) => operation.transaction)
3361
+ : input.instructions.map((instruction) => isPreparedInstruction(instruction)
3362
+ ? instruction.transaction
3363
+ : createPreparedTransactionBody({
3364
+ name: input.name,
3365
+ instructions: [instruction],
3366
+ }));
3367
+ const transaction = createPreparedTransactionBody({
3368
+ name: input.name,
3369
+ instructions: transactionParts.flatMap((part) => part.instructions),
3370
+ requiredSignerAddresses: input.requiredSignerAddresses
3371
+ ?? transactionParts.flatMap((part) => part.requiredSignerAddresses),
3372
+ errors: input.errors ?? transactionParts.flatMap((part) => part.errors),
3373
+ });
3374
+ return {
3375
+ kind: 'transaction',
3376
+ name: input.name,
3377
+ transaction,
3378
+ plan: createPlan(input.name, input.artifacts, [transaction]),
3379
+ artifacts: input.artifacts,
3380
+ };
3381
+ }
3382
+ function createPreparedFlow(input) {
3383
+ const transactions = input.transactions.map((transaction) => createPreparedTransactionBody(transaction));
3384
+ return {
3385
+ kind: 'flow',
3386
+ name: input.name,
3387
+ plan: createPlan(input.name, input.artifacts, transactions),
3388
+ artifacts: input.artifacts,
3389
+ };
3390
+ }
3391
+ function prependTransactionInstructions(transaction, instructions) {
3392
+ return createPreparedTransactionBody({
3393
+ ...transaction,
3394
+ instructions: [...instructions, ...transaction.instructions],
3395
+ requiredSignerAddresses: [
3396
+ ...inferSignerAddresses(instructions),
3397
+ ...transaction.requiredSignerAddresses,
3398
+ ],
3399
+ });
3400
+ }
3401
+ function appendTransactionInstructions(transaction, instructions) {
3402
+ return createPreparedTransactionBody({
3403
+ ...transaction,
3404
+ instructions: [...transaction.instructions, ...instructions],
3405
+ requiredSignerAddresses: [
3406
+ ...transaction.requiredSignerAddresses,
3407
+ ...inferSignerAddresses(instructions),
3408
+ ],
3409
+ });
3410
+ }
3411
+ function appendFlowTransactions(flow, transactions) {
3412
+ return createPreparedFlow({
3413
+ name: flow.name,
3414
+ artifacts: flow.artifacts,
3415
+ transactions: [...flow.plan.transactions, ...transactions],
3416
+ });
3417
+ }
3418
+ function prependFlowTransactionInstructions(flow, transactionIndex, instructions) {
3419
+ const transaction = flow.plan.transactions[transactionIndex];
3420
+ if (!transaction) {
3421
+ throw new Error(`Flow '${flow.name}' has no transaction at index ${transactionIndex}`);
3422
+ }
3423
+ const transactions = [...flow.plan.transactions];
3424
+ transactions[transactionIndex] = prependTransactionInstructions(transaction, instructions);
3425
+ return createPreparedFlow({
3426
+ name: flow.name,
3427
+ artifacts: flow.artifacts,
3428
+ transactions,
3429
+ });
3430
+ }
3431
+ function inferSignerAddress(value) {
3432
+ if (typeof value === 'string' && value.length > 0) {
3433
+ return value;
2573
3434
  }
2574
- get store() {
2575
- return this.storage;
3435
+ if (!value || typeof value !== 'object') {
3436
+ return null;
2576
3437
  }
2577
- onConnectionStateChange(callback) {
2578
- return this.connection.onStateChange(callback);
3438
+ const candidate = value;
3439
+ if (typeof candidate.address === 'string' && candidate.address.length > 0) {
3440
+ return candidate.address;
2579
3441
  }
2580
- onFrame(callback) {
2581
- return this.connection.onFrame(callback);
3442
+ if (typeof candidate.publicKey === 'string' && candidate.publicKey.length > 0) {
3443
+ return candidate.publicKey;
2582
3444
  }
2583
- onSocketIssue(callback) {
2584
- return this.connection.onSocketIssue(callback);
3445
+ const publicKey = candidate.publicKey;
3446
+ return typeof publicKey?.toBase58 === 'function' ? publicKey.toBase58() : null;
3447
+ }
3448
+ function validateTransactionSigners(transaction, host, options) {
3449
+ const signerAddresses = (options.signers ?? []).map(inferSignerAddress);
3450
+ const available = new Set(options.availableSignerAddresses ?? []);
3451
+ for (const address of options.signerRegistry?.addresses() ?? []) {
3452
+ available.add(address);
2585
3453
  }
2586
- async connect() {
2587
- await this.connection.connect();
3454
+ const wallet = options.wallet ?? host.wallet;
3455
+ for (const address of wallet?.signerAddresses ?? []) {
3456
+ available.add(address);
2588
3457
  }
2589
- disconnect() {
2590
- this.subscriptionRegistry.clear();
2591
- this.connection.disconnect();
3458
+ const walletAddress = wallet?.publicKey ?? host.publicKey;
3459
+ if (walletAddress) {
3460
+ available.add(walletAddress);
2592
3461
  }
2593
- isConnected() {
2594
- return this.connection.isConnected();
3462
+ for (const address of signerAddresses) {
3463
+ if (address) {
3464
+ available.add(address);
3465
+ }
2595
3466
  }
2596
- clearStore() {
2597
- this.storage.clear();
3467
+ const missing = transaction.requiredSignerAddresses.filter((address) => !available.has(address));
3468
+ if (missing.length > 0) {
3469
+ throw new Error(`Missing signer(s) for ${transaction.name}: ${missing.join(', ')}`);
2598
3470
  }
2599
- getStore() {
2600
- return this.storage;
3471
+ }
3472
+ class OperationExecutionError extends Error {
3473
+ constructor(input) {
3474
+ super(`Operation '${input.operation.name}' failed at transaction ${input.failedTransactionIndex + 1} (${input.failedTransaction.name})`);
3475
+ this.name = 'OperationExecutionError';
3476
+ this.operation = input.operation;
3477
+ this.failedTransaction = input.failedTransaction;
3478
+ this.failedTransactionIndex = input.failedTransactionIndex;
3479
+ this.completedReceipts = [...input.completedReceipts];
3480
+ this.cause = input.cause;
2601
3481
  }
2602
- getConnection() {
2603
- return this.connection;
3482
+ }
3483
+ async function executePreparedOperation(host, operation, options = {}) {
3484
+ const receipts = [];
3485
+ const signers = [
3486
+ ...new Set([
3487
+ ...(options.signerRegistry?.values() ?? []),
3488
+ ...(options.signers ?? []),
3489
+ ]),
3490
+ ];
3491
+ for (const [transactionIndex, transaction] of operation.plan.transactions.entries()) {
3492
+ try {
3493
+ validateTransactionSigners(transaction, host, options);
3494
+ await options.onTransactionStart?.({ operation, transaction, transactionIndex });
3495
+ const result = await host.transaction(transaction.instructions, {
3496
+ wallet: options.wallet,
3497
+ send: options.send,
3498
+ errors: [...transaction.errors],
3499
+ signers: signers.length > 0 ? signers : undefined,
3500
+ });
3501
+ const receipt = {
3502
+ transactionIndex,
3503
+ transactionName: transaction.name,
3504
+ signature: result.signature,
3505
+ slot: result.slot,
3506
+ };
3507
+ receipts.push(receipt);
3508
+ await options.onTransactionSuccess?.({
3509
+ operation,
3510
+ transaction,
3511
+ transactionIndex,
3512
+ receipt,
3513
+ });
3514
+ }
3515
+ catch (cause) {
3516
+ throw new OperationExecutionError({
3517
+ operation,
3518
+ failedTransaction: transaction,
3519
+ failedTransactionIndex: transactionIndex,
3520
+ completedReceipts: receipts,
3521
+ cause,
3522
+ });
3523
+ }
2604
3524
  }
2605
- getSubscriptionRegistry() {
2606
- return this.subscriptionRegistry;
3525
+ if (operation.kind === 'flow') {
3526
+ return {
3527
+ kind: 'flow',
3528
+ operationName: operation.name,
3529
+ artifacts: operation.artifacts,
3530
+ signatures: nonEmpty(receipts.map((receipt) => receipt.signature), `Operation '${operation.name}' signatures`),
3531
+ transactions: nonEmpty(receipts, `Operation '${operation.name}' receipts`),
3532
+ };
2607
3533
  }
3534
+ return {
3535
+ kind: operation.kind,
3536
+ operationName: operation.name,
3537
+ artifacts: operation.artifacts,
3538
+ signatures: [receipts[0].signature],
3539
+ transaction: receipts[0],
3540
+ };
2608
3541
  }
2609
-
2610
- function getNestedValue(obj, path) {
2611
- let current = obj;
2612
- for (const segment of path) {
2613
- if (current === null || current === undefined)
2614
- return undefined;
2615
- if (typeof current !== 'object')
2616
- return undefined;
2617
- current = current[segment];
3542
+ function convertToJsonValue(value, ancestors) {
3543
+ if (value === null || typeof value === 'string' || typeof value === 'boolean') {
3544
+ return value;
2618
3545
  }
2619
- return current;
2620
- }
2621
- function compareSortValues(a, b) {
2622
- if (a === b)
2623
- return 0;
2624
- if (a === undefined || a === null)
2625
- return -1;
2626
- if (b === undefined || b === null)
2627
- return 1;
2628
- if (typeof a === 'number' && typeof b === 'number') {
2629
- return a - b;
3546
+ if (typeof value === 'number') {
3547
+ return Number.isFinite(value) ? value : null;
2630
3548
  }
2631
- if (typeof a === 'string' && typeof b === 'string') {
2632
- return a.localeCompare(b);
3549
+ if (typeof value === 'bigint') {
3550
+ return value.toString();
2633
3551
  }
2634
- if (typeof a === 'boolean' && typeof b === 'boolean') {
2635
- return (a ? 1 : 0) - (b ? 1 : 0);
3552
+ if (value === undefined
3553
+ || typeof value === 'function'
3554
+ || typeof value === 'symbol') {
3555
+ return undefined;
2636
3556
  }
2637
- return String(a).localeCompare(String(b));
2638
- }
2639
- class ViewData {
2640
- constructor(sortConfig) {
2641
- this.entities = new Map();
2642
- this.accessOrder = [];
2643
- this.sortedKeys = [];
2644
- this.sortConfig = sortConfig;
3557
+ if (typeof value !== 'object') {
3558
+ return String(value);
2645
3559
  }
2646
- get(key) {
2647
- return this.entities.get(key);
3560
+ if (ancestors.has(value)) {
3561
+ throw new Error('Cannot convert a circular value to JSON');
2648
3562
  }
2649
- set(key, value) {
2650
- const isNew = !this.entities.has(key);
2651
- this.entities.set(key, value);
2652
- if (this.sortConfig) {
2653
- this.updateSortedPosition(key, value, isNew);
3563
+ ancestors.add(value);
3564
+ try {
3565
+ if (ArrayBuffer.isView(value)) {
3566
+ return Array.from(new Uint8Array(value.buffer, value.byteOffset, value.byteLength));
2654
3567
  }
2655
- else {
2656
- if (isNew) {
2657
- this.accessOrder.push(key);
2658
- }
2659
- else {
2660
- this.touch(key);
2661
- }
3568
+ if (value instanceof ArrayBuffer) {
3569
+ return Array.from(new Uint8Array(value));
2662
3570
  }
2663
- }
2664
- updateSortedPosition(key, value, isNew) {
2665
- if (!isNew) {
2666
- const existingIdx = this.sortedKeys.indexOf(key);
2667
- if (existingIdx !== -1) {
2668
- this.sortedKeys.splice(existingIdx, 1);
2669
- }
3571
+ if (Array.isArray(value)) {
3572
+ return value.map((entry) => convertToJsonValue(entry, ancestors) ?? null);
2670
3573
  }
2671
- const sortValue = getNestedValue(value, this.sortConfig.field);
2672
- const isDesc = this.sortConfig.order === 'desc';
2673
- let insertIdx = this.binarySearchInsertPosition(sortValue, key, isDesc);
2674
- this.sortedKeys.splice(insertIdx, 0, key);
2675
- }
2676
- binarySearchInsertPosition(sortValue, key, isDesc) {
2677
- let low = 0;
2678
- let high = this.sortedKeys.length;
2679
- while (low < high) {
2680
- const mid = Math.floor((low + high) / 2);
2681
- const midKey = this.sortedKeys[mid];
2682
- if (midKey === undefined)
2683
- break;
2684
- const midEntity = this.entities.get(midKey);
2685
- const midValue = getNestedValue(midEntity, this.sortConfig.field);
2686
- let cmp = compareSortValues(sortValue, midValue);
2687
- if (isDesc)
2688
- cmp = -cmp;
2689
- if (cmp === 0) {
2690
- cmp = key.localeCompare(midKey);
2691
- }
2692
- if (cmp < 0) {
2693
- high = mid;
2694
- }
2695
- else {
2696
- low = mid + 1;
2697
- }
3574
+ if (value instanceof Set) {
3575
+ return [...value].map((entry) => convertToJsonValue(entry, ancestors) ?? null);
2698
3576
  }
2699
- return low;
2700
- }
2701
- delete(key) {
2702
- if (this.sortConfig) {
2703
- const idx = this.sortedKeys.indexOf(key);
2704
- if (idx !== -1) {
2705
- this.sortedKeys.splice(idx, 1);
3577
+ if (value instanceof Map) {
3578
+ const result = {};
3579
+ for (const [key, entry] of value) {
3580
+ const converted = convertToJsonValue(entry, ancestors);
3581
+ if (converted !== undefined) {
3582
+ result[String(key)] = converted;
3583
+ }
2706
3584
  }
3585
+ return result;
2707
3586
  }
2708
- else {
2709
- const idx = this.accessOrder.indexOf(key);
2710
- if (idx !== -1) {
2711
- this.accessOrder.splice(idx, 1);
3587
+ const toJSON = value.toJSON;
3588
+ if (typeof toJSON === 'function') {
3589
+ return convertToJsonValue(toJSON.call(value), ancestors) ?? null;
3590
+ }
3591
+ const result = {};
3592
+ for (const [key, entry] of Object.entries(value)) {
3593
+ const converted = convertToJsonValue(entry, ancestors);
3594
+ if (converted !== undefined) {
3595
+ result[key] = converted;
2712
3596
  }
2713
3597
  }
2714
- return this.entities.delete(key);
3598
+ return result;
2715
3599
  }
2716
- has(key) {
2717
- return this.entities.has(key);
3600
+ finally {
3601
+ ancestors.delete(value);
2718
3602
  }
2719
- values() {
2720
- if (this.sortConfig) {
2721
- return this.sortedKeys.map(k => this.entities.get(k));
3603
+ }
3604
+ function toJsonValue(value) {
3605
+ return convertToJsonValue(value, new Set()) ?? null;
3606
+ }
3607
+ function describePreparedOperation(operation) {
3608
+ return {
3609
+ kind: operation.kind,
3610
+ name: operation.name,
3611
+ artifacts: toJsonValue(operation.artifacts),
3612
+ transactions: operation.plan.transactions.map((transaction) => ({
3613
+ name: transaction.name,
3614
+ requiredSignerAddresses: transaction.requiredSignerAddresses,
3615
+ errors: transaction.errors,
3616
+ instructions: transaction.instructions.map((instruction) => ({
3617
+ programId: instruction.programId,
3618
+ keys: instruction.keys,
3619
+ data: Array.from(instruction.data),
3620
+ })),
3621
+ })),
3622
+ };
3623
+ }
3624
+ function formatPreparedOperation(operation) {
3625
+ const lines = [
3626
+ `${operation.kind}: ${operation.name}`,
3627
+ `Transactions: ${operation.plan.transactions.length}`,
3628
+ ];
3629
+ for (const [index, transaction] of operation.plan.transactions.entries()) {
3630
+ lines.push(` ${index + 1}. ${transaction.name} (${transaction.instructions.length} instruction${transaction.instructions.length === 1 ? '' : 's'})`);
3631
+ if (transaction.requiredSignerAddresses.length > 0) {
3632
+ lines.push(` Signers: ${transaction.requiredSignerAddresses.join(', ')}`);
3633
+ }
3634
+ }
3635
+ return lines.join('\n');
3636
+ }
3637
+
3638
+ function mergeAttachedPrograms(stack, attachedPrograms) {
3639
+ const merged = { ...(attachedPrograms ?? {}) };
3640
+ for (const [name, definition] of Object.entries(stack.programs ?? {})) {
3641
+ if (name in merged) {
3642
+ console.warn(`Ignoring attached program '${name}' for stack '${stack.name}' because the stack already defines that key`);
2722
3643
  }
2723
- return Array.from(this.entities.values());
3644
+ merged[name] = definition;
2724
3645
  }
2725
- keys() {
2726
- if (this.sortConfig) {
2727
- return [...this.sortedKeys];
2728
- }
2729
- return Array.from(this.entities.keys());
3646
+ return merged;
3647
+ }
3648
+ function normalizeProgramAccountWireKeys(value) {
3649
+ if (Array.isArray(value)) {
3650
+ return value.map(normalizeProgramAccountWireKeys);
2730
3651
  }
2731
- get size() {
2732
- return this.entities.size;
3652
+ if (!value || typeof value !== 'object') {
3653
+ return value;
2733
3654
  }
2734
- touch(key) {
2735
- if (this.sortConfig)
2736
- return;
2737
- const idx = this.accessOrder.indexOf(key);
2738
- if (idx !== -1) {
2739
- this.accessOrder.splice(idx, 1);
2740
- this.accessOrder.push(key);
2741
- }
3655
+ return Object.fromEntries(Object.entries(value).map(([key, nestedValue]) => [
3656
+ key.replace(/[A-Z]/g, (letter, index) => `${index === 0 ? '' : '_'}${letter.toLowerCase()}`),
3657
+ normalizeProgramAccountWireKeys(nestedValue),
3658
+ ]));
3659
+ }
3660
+ function parseProgramAccountValue(definition, value) {
3661
+ const schema = definition.schema;
3662
+ if (!schema) {
3663
+ return value;
2742
3664
  }
2743
- evictOldest() {
2744
- if (this.sortConfig) {
2745
- const oldest = this.sortedKeys.pop();
2746
- if (oldest !== undefined) {
2747
- this.entities.delete(oldest);
2748
- }
2749
- return oldest;
2750
- }
2751
- const oldest = this.accessOrder.shift();
2752
- if (oldest !== undefined) {
2753
- this.entities.delete(oldest);
2754
- }
2755
- return oldest;
3665
+ const parsed = schema.safeParse(value);
3666
+ if (parsed.success) {
3667
+ return parsed.data;
2756
3668
  }
2757
- setSortConfig(config) {
2758
- if (this.sortConfig)
2759
- return;
2760
- this.sortConfig = config;
2761
- this.rebuildSortedKeys();
3669
+ const normalized = schema.safeParse(normalizeProgramAccountWireKeys(value));
3670
+ if (normalized.success) {
3671
+ return normalized.data;
2762
3672
  }
2763
- rebuildSortedKeys() {
2764
- if (!this.sortConfig)
2765
- return;
2766
- const entries = Array.from(this.entities.entries());
2767
- const isDesc = this.sortConfig.order === 'desc';
2768
- entries.sort((a, b) => {
2769
- const aValue = getNestedValue(a[1], this.sortConfig.field);
2770
- const bValue = getNestedValue(b[1], this.sortConfig.field);
2771
- let cmp = compareSortValues(aValue, bValue);
2772
- if (isDesc)
2773
- cmp = -cmp;
2774
- if (cmp === 0) {
2775
- cmp = a[0].localeCompare(b[0]);
2776
- }
2777
- return cmp;
3673
+ throw new Error(`Program account read '${definition.account}' failed schema validation`);
3674
+ }
3675
+ function cloneStackWithPrograms(stack, programs) {
3676
+ const cloned = Object.create(Object.getPrototypeOf(stack), Object.getOwnPropertyDescriptors(stack));
3677
+ cloned.programs = programs;
3678
+ return cloned;
3679
+ }
3680
+ function withPrograms(stack, attachedPrograms) {
3681
+ return cloneStackWithPrograms(stack, mergeAttachedPrograms(stack, attachedPrograms));
3682
+ }
3683
+ let Arete$1 = class Arete {
3684
+ constructor(url, httpBaseUrl, options) {
3685
+ this.stack = options.stack;
3686
+ this._wallet = options.wallet;
3687
+ this.executionDefaults = options.execution;
3688
+ this.httpBaseUrl = httpBaseUrl;
3689
+ this.fetchImpl = options.fetch ?? this.resolveFetchImpl();
3690
+ this.storage = new SortedStorageDecorator(options.storage ?? new MemoryAdapter());
3691
+ this.processor = new FrameProcessor(this.storage, {
3692
+ maxEntriesPerView: options.maxEntriesPerView,
3693
+ flushIntervalMs: options.flushIntervalMs,
3694
+ schemas: this.stack.schemas,
3695
+ patchSchemas: this.stack.patchSchemas,
2778
3696
  });
2779
- this.sortedKeys = entries.map(([k]) => k);
2780
- this.accessOrder = [];
3697
+ this.connection = new ConnectionManager({
3698
+ websocketUrl: url,
3699
+ reconnectIntervals: options.reconnectIntervals,
3700
+ maxReconnectAttempts: options.maxReconnectAttempts,
3701
+ auth: options.auth,
3702
+ });
3703
+ this.subscriptionRegistry = new SubscriptionRegistry(this.connection);
3704
+ this.connection.onFrame((frame) => {
3705
+ this.processor.handleFrame(frame);
3706
+ });
3707
+ this._views = createTypedViews(this.stack, this.storage, this.subscriptionRegistry);
3708
+ this._queries = this.buildQueries();
3709
+ this._chain = createChainClient(this.httpBaseUrl, this.authenticatedFetch.bind(this));
3710
+ this._programs = this.buildPrograms();
2781
3711
  }
2782
- getSortConfig() {
2783
- return this.sortConfig;
3712
+ resolveFetchImpl() {
3713
+ if (typeof globalThis.fetch !== 'function') {
3714
+ throw new AreteError('A fetch implementation is required for HTTP point reads (provide ConnectOptions.fetch or use an environment with global fetch)', 'INVALID_CONFIG');
3715
+ }
3716
+ return globalThis.fetch.bind(globalThis);
2784
3717
  }
2785
- }
2786
- function isObject(item) {
2787
- return item !== null && typeof item === 'object' && !Array.isArray(item);
2788
- }
2789
- function deepMergeWithAppend(target, source, appendPaths, currentPath = '') {
2790
- if (!isObject(target) || !isObject(source)) {
2791
- return source;
3718
+ buildQueries() {
3719
+ const queries = {};
3720
+ for (const [name, definition] of Object.entries(this.stack.queries ?? {})) {
3721
+ queries[name] = this.createQueryExecutor(definition);
3722
+ }
3723
+ return queries;
2792
3724
  }
2793
- const result = { ...target };
2794
- for (const key in source) {
2795
- const sourceValue = source[key];
2796
- const targetValue = result[key];
2797
- const fieldPath = currentPath ? `${currentPath}.${key}` : key;
2798
- if (Array.isArray(sourceValue) && Array.isArray(targetValue)) {
2799
- if (appendPaths.includes(fieldPath)) {
2800
- result[key] = [...targetValue, ...sourceValue];
3725
+ buildPrograms() {
3726
+ const bases = {};
3727
+ for (const [name, definition] of Object.entries(this.stack.programs ?? {})) {
3728
+ const instructions = {};
3729
+ for (const [instructionName, handler] of Object.entries(definition.rawInstructions ?? {})) {
3730
+ instructions[instructionName] = this.createTypedInstruction(handler);
2801
3731
  }
2802
- else {
2803
- result[key] = sourceValue;
3732
+ const accounts = {};
3733
+ for (const [accountName, accountDefinition] of Object.entries(definition.accounts ?? {})) {
3734
+ accounts[accountName] = this.createAccountReader(accountDefinition);
2804
3735
  }
3736
+ const queries = {};
3737
+ for (const [queryName, queryDefinition] of Object.entries(definition.queries ?? {})) {
3738
+ queries[queryName] = this.createQueryExecutor(queryDefinition);
3739
+ }
3740
+ bases[name] = {
3741
+ name: definition.name,
3742
+ programId: definition.programId,
3743
+ schemas: definition.schemas,
3744
+ pdas: definition.pdas ?? {},
3745
+ accounts,
3746
+ queries,
3747
+ raw: instructions,
3748
+ addresses: definition.addresses ?? {},
3749
+ constants: definition.constants ?? {},
3750
+ defaults: definition.defaults ?? {},
3751
+ math: definition.math ?? {},
3752
+ };
2805
3753
  }
2806
- else if (isObject(sourceValue) && isObject(targetValue)) {
2807
- result[key] = deepMergeWithAppend(targetValue, sourceValue, appendPaths, fieldPath);
2808
- }
2809
- else {
2810
- result[key] = sourceValue;
3754
+ const programs = {};
3755
+ const client = this;
3756
+ for (const [name, definition] of Object.entries(this.stack.programs ?? {})) {
3757
+ const base = bases[name];
3758
+ const connectedProgram = {
3759
+ ...base,
3760
+ instructions: {},
3761
+ transactions: {},
3762
+ flows: {},
3763
+ };
3764
+ const runtime = getProgramRuntimeExtensions(definition);
3765
+ const operations = runtime?.createOperations({
3766
+ chain: this._chain,
3767
+ get wallet() {
3768
+ return client._wallet;
3769
+ },
3770
+ program: connectedProgram,
3771
+ });
3772
+ connectedProgram.instructions = operations?.instructions ?? {};
3773
+ connectedProgram.transactions = operations?.transactions ?? {};
3774
+ connectedProgram.flows = operations?.flows ?? {};
3775
+ programs[name] = connectedProgram;
2811
3776
  }
3777
+ return programs;
2812
3778
  }
2813
- return result;
2814
- }
2815
- class EntityStore {
2816
- constructor(config = {}) {
2817
- this.views = new Map();
2818
- this.viewConfigs = new Map();
2819
- this.updateCallbacks = new Set();
2820
- this.richUpdateCallbacks = new Set();
2821
- this.maxEntriesPerView = config.maxEntriesPerView === undefined
2822
- ? DEFAULT_MAX_ENTRIES_PER_VIEW
2823
- : config.maxEntriesPerView;
3779
+ createTypedInstruction(handler) {
3780
+ return {
3781
+ build: (params, options) => buildInstruction(handler, params, this.withWallet(options)),
3782
+ };
2824
3783
  }
2825
- enforceMaxEntries(viewData) {
2826
- if (this.maxEntriesPerView === null)
2827
- return;
2828
- while (viewData.size > this.maxEntriesPerView) {
2829
- viewData.evictOldest();
2830
- }
3784
+ createAccountReader(definition) {
3785
+ return {
3786
+ fetch: async (address) => {
3787
+ const result = await this.readJson(`${definition.path}/${encodeURIComponent(address)}`);
3788
+ return result === null ? null : parseProgramAccountValue(definition, result);
3789
+ },
3790
+ fetchMany: async (addresses) => {
3791
+ const result = await this.readJson(definition.path, {
3792
+ method: 'POST',
3793
+ body: { addresses },
3794
+ });
3795
+ return result.map((value) => value === null ? null : parseProgramAccountValue(definition, value));
3796
+ },
3797
+ exists: async (address) => {
3798
+ const result = await this.readJson(`${definition.path}/${encodeURIComponent(address)}/exists`);
3799
+ return result.exists;
3800
+ },
3801
+ };
2831
3802
  }
2832
- handleFrame(frame) {
2833
- if (isSubscribedFrame(frame)) {
2834
- this.handleSubscribedFrame(frame);
2835
- return;
2836
- }
2837
- if (isSnapshotFrame(frame)) {
2838
- this.handleSnapshotFrame(frame);
2839
- return;
2840
- }
2841
- this.handleEntityFrame(frame);
3803
+ createQueryExecutor(definition) {
3804
+ return async (params) => {
3805
+ const result = await this.readJson(definition.path, {
3806
+ method: definition.method ?? 'POST',
3807
+ body: params,
3808
+ });
3809
+ if (!definition.schema) {
3810
+ return result;
3811
+ }
3812
+ const parsed = definition.schema.safeParse(result);
3813
+ if (!parsed.success) {
3814
+ throw new Error(`Query '${definition.name}' failed schema validation`);
3815
+ }
3816
+ return parsed.data;
3817
+ };
2842
3818
  }
2843
- handleSubscribedFrame(frame) {
2844
- const viewPath = frame.view;
2845
- const config = {};
2846
- if (frame.sort) {
2847
- config.sort = frame.sort;
2848
- }
2849
- this.viewConfigs.set(viewPath, config);
2850
- const existingView = this.views.get(viewPath);
2851
- if (existingView && frame.sort) {
2852
- existingView.setSortConfig(frame.sort);
3819
+ async readJson(path, options) {
3820
+ const response = await this.authenticatedFetch(this.resolveReadUrl(path), {
3821
+ method: options?.method ?? 'GET',
3822
+ headers: options?.body === undefined ? undefined : { 'content-type': 'application/json' },
3823
+ body: options?.body === undefined ? undefined : JSON.stringify(options.body),
3824
+ });
3825
+ return parseReadResponse(response, path);
3826
+ }
3827
+ async authenticatedFetch(input, init) {
3828
+ const attempt = async (forceRefresh = false) => {
3829
+ const token = await this.connection.getHttpAuthToken(forceRefresh);
3830
+ const headers = new Headers(init?.headers ?? undefined);
3831
+ if (token) {
3832
+ headers.set('authorization', `Bearer ${token}`);
3833
+ }
3834
+ return this.fetchImpl(input, {
3835
+ ...init,
3836
+ headers,
3837
+ });
3838
+ };
3839
+ let response = await attempt(false);
3840
+ if (!response.ok) {
3841
+ const wireErrorCode = response.headers.get('X-Error-Code');
3842
+ const errorCode = wireErrorCode ? parseErrorCode(wireErrorCode) : undefined;
3843
+ if (errorCode && shouldRefreshToken(errorCode)) {
3844
+ this.connection.clearHttpAuthToken();
3845
+ response = await attempt(true);
3846
+ }
2853
3847
  }
3848
+ return response;
2854
3849
  }
2855
- handleSnapshotFrame(frame) {
2856
- const viewPath = frame.entity;
2857
- let viewData = this.views.get(viewPath);
2858
- const viewConfig = this.viewConfigs.get(viewPath);
2859
- if (!viewData) {
2860
- viewData = new ViewData(viewConfig?.sort);
2861
- this.views.set(viewPath, viewData);
3850
+ resolveReadUrl(path) {
3851
+ return `${this.httpBaseUrl.replace(/\/$/, '')}${path.startsWith('/') ? path : `/${path}`}`;
3852
+ }
3853
+ /** Merge the client's default wallet into call options (call options win). */
3854
+ withWallet(options) {
3855
+ const merged = { ...(options ?? {}) };
3856
+ if (!merged.wallet && this._wallet) {
3857
+ merged.wallet = this._wallet;
2862
3858
  }
2863
- for (const entity of frame.data) {
2864
- const previousValue = viewData.get(entity.key);
2865
- viewData.set(entity.key, entity.data);
2866
- this.notifyUpdate(viewPath, entity.key, {
2867
- type: 'upsert',
2868
- key: entity.key,
2869
- data: entity.data,
2870
- });
2871
- this.notifyRichUpdate(viewPath, entity.key, previousValue, entity.data, 'upsert');
3859
+ return merged;
3860
+ }
3861
+ static async connect(stack, options) {
3862
+ const requestedUrl = options?.url ?? stack.endpoints.ws;
3863
+ const autoReconnect = options?.autoReconnect !== false;
3864
+ const httpOnly = options?.transport === 'http' || (!requestedUrl && !autoReconnect);
3865
+ const url = httpOnly ? null : requestedUrl;
3866
+ const httpUrl = options?.httpUrl
3867
+ ?? stack.endpoints.http
3868
+ ?? (requestedUrl ? deriveHttpEndpoint(requestedUrl) : undefined);
3869
+ if (!httpOnly && !url) {
3870
+ throw new AreteError('WebSocket URL is required (provide url option or define endpoints.ws in stack)', 'INVALID_CONFIG');
3871
+ }
3872
+ if (!httpUrl) {
3873
+ throw new AreteError('HTTP endpoint is required for transport: "http" (provide httpUrl option or define endpoints.http in stack)', 'INVALID_CONFIG');
3874
+ }
3875
+ const attachedPrograms = options?.programs;
3876
+ const effectiveStack = withPrograms(stack, attachedPrograms);
3877
+ const internalOptions = {
3878
+ stack: effectiveStack,
3879
+ httpUrl,
3880
+ storage: options?.storage,
3881
+ maxEntriesPerView: options?.maxEntriesPerView,
3882
+ flushIntervalMs: options?.flushIntervalMs,
3883
+ autoReconnect: options?.autoReconnect,
3884
+ reconnectIntervals: options?.reconnectIntervals,
3885
+ maxReconnectAttempts: options?.maxReconnectAttempts,
3886
+ validateFrames: options?.validateFrames,
3887
+ auth: options?.auth,
3888
+ wallet: options?.wallet,
3889
+ fetch: options?.fetch,
3890
+ execution: options?.execution,
3891
+ };
3892
+ const client = new Arete(url, httpUrl, internalOptions);
3893
+ if (!httpOnly && autoReconnect) {
3894
+ await client.connection.connect();
2872
3895
  }
2873
- this.enforceMaxEntries(viewData);
3896
+ return applyConnectedStackExtensions(client, effectiveStack);
2874
3897
  }
2875
- handleEntityFrame(frame) {
2876
- const viewPath = frame.entity;
2877
- let viewData = this.views.get(viewPath);
2878
- const viewConfig = this.viewConfigs.get(viewPath);
2879
- if (!viewData) {
2880
- viewData = new ViewData(viewConfig?.sort);
2881
- this.views.set(viewPath, viewData);
3898
+ get views() {
3899
+ return this._views;
3900
+ }
3901
+ get queries() {
3902
+ return this._queries;
3903
+ }
3904
+ get programs() {
3905
+ return this._programs;
3906
+ }
3907
+ get chain() {
3908
+ return this._chain;
3909
+ }
3910
+ /** The default wallet adapter, if one was configured. */
3911
+ get wallet() {
3912
+ return this._wallet;
3913
+ }
3914
+ /** The connected wallet address, if a default wallet was configured. */
3915
+ get publicKey() {
3916
+ return this._wallet?.publicKey;
3917
+ }
3918
+ /**
3919
+ * Set (or clear) the default wallet adapter used for instruction execution.
3920
+ * Useful for connecting/disconnecting a wallet after the client is created.
3921
+ */
3922
+ setWallet(wallet) {
3923
+ this._wallet = wallet;
3924
+ }
3925
+ /**
3926
+ * Sign and send a batch of pre-built instructions as a single transaction.
3927
+ *
3928
+ * Build instructions with `client.programs.<program>.raw.<name>.build(params)`
3929
+ * and compose them here. RPC/compilation/confirmation are owned by the adapter.
3930
+ *
3931
+ * On failure, the error is parsed against `options.errors` when given,
3932
+ * otherwise against error metadata aggregated from all the stack's handlers
3933
+ * (deduped by code, first-wins — if the stack bundles programs with
3934
+ * overlapping error codes, pass `options.errors` or use the per-instruction
3935
+ * call path for precise attribution).
3936
+ */
3937
+ async transaction(instructions, options) {
3938
+ const wallet = options?.wallet ?? this._wallet;
3939
+ if (!wallet) {
3940
+ throw new Error('Wallet required to sign and send transaction');
2882
3941
  }
2883
- const previousValue = viewData.get(frame.key);
2884
- switch (frame.op) {
2885
- case 'create':
2886
- case 'upsert':
2887
- viewData.set(frame.key, frame.data);
2888
- this.enforceMaxEntries(viewData);
2889
- this.notifyUpdate(viewPath, frame.key, {
2890
- type: 'upsert',
2891
- key: frame.key,
2892
- data: frame.data,
2893
- });
2894
- this.notifyRichUpdate(viewPath, frame.key, previousValue, frame.data, frame.op);
2895
- break;
2896
- case 'patch': {
2897
- const existing = viewData.get(frame.key);
2898
- const appendPaths = frame.append ?? [];
2899
- const merged = existing
2900
- ? deepMergeWithAppend(existing, frame.data, appendPaths)
2901
- : frame.data;
2902
- viewData.set(frame.key, merged);
2903
- this.enforceMaxEntries(viewData);
2904
- this.notifyUpdate(viewPath, frame.key, {
2905
- type: 'patch',
2906
- key: frame.key,
2907
- data: frame.data,
2908
- });
2909
- this.notifyRichUpdate(viewPath, frame.key, previousValue, merged, 'patch', frame.data);
2910
- break;
3942
+ try {
3943
+ const sendOptions = {
3944
+ ...(options?.send ?? {}),
3945
+ };
3946
+ if (options?.signers !== undefined) {
3947
+ sendOptions.signers = options.signers;
2911
3948
  }
2912
- case 'delete':
2913
- viewData.delete(frame.key);
2914
- this.notifyUpdate(viewPath, frame.key, {
2915
- type: 'delete',
2916
- key: frame.key,
2917
- });
2918
- if (previousValue !== undefined) {
2919
- this.notifyRichDelete(viewPath, frame.key, previousValue);
3949
+ const result = await wallet.signAndSend(instructions, sendOptions);
3950
+ return { signature: result.signature, slot: result.slot };
3951
+ }
3952
+ catch (err) {
3953
+ const programError = parseInstructionError(err, options?.errors ?? this.aggregateErrors());
3954
+ if (programError) {
3955
+ throw new InstructionError(`${programError.name} (${programError.code}): ${programError.message}`, programError, err);
3956
+ }
3957
+ throw err;
3958
+ }
3959
+ }
3960
+ execute(prepared, options) {
3961
+ const defaults = this.executionDefaults;
3962
+ return executePreparedOperation(this, prepared, {
3963
+ wallet: options?.wallet ?? defaults?.wallet,
3964
+ send: defaults?.send || options?.send
3965
+ ? { ...(defaults?.send ?? {}), ...(options?.send ?? {}) }
3966
+ : undefined,
3967
+ signers: options?.signers ?? defaults?.signers,
3968
+ signerRegistry: options?.signerRegistry ?? defaults?.signerRegistry,
3969
+ availableSignerAddresses: options?.availableSignerAddresses ?? defaults?.availableSignerAddresses,
3970
+ onTransactionStart: options?.onTransactionStart ?? defaults?.onTransactionStart,
3971
+ onTransactionSuccess: options?.onTransactionSuccess ?? defaults?.onTransactionSuccess,
3972
+ });
3973
+ }
3974
+ /** Error metadata from every handler in the stack, deduped by code. */
3975
+ aggregateErrors() {
3976
+ if (!this._aggregatedErrors) {
3977
+ const all = [];
3978
+ const seen = new Set();
3979
+ const collect = (handler) => {
3980
+ for (const error of handler.errors ?? []) {
3981
+ if (!seen.has(error.code)) {
3982
+ seen.add(error.code);
3983
+ all.push(error);
3984
+ }
2920
3985
  }
2921
- break;
3986
+ };
3987
+ for (const program of Object.values(this.stack.programs ?? {})) {
3988
+ for (const handler of Object.values(program.rawInstructions ?? {})) {
3989
+ collect(handler);
3990
+ }
3991
+ }
3992
+ this._aggregatedErrors = all;
2922
3993
  }
3994
+ return this._aggregatedErrors;
2923
3995
  }
2924
- getAll(viewPath) {
2925
- const viewData = this.views.get(viewPath);
2926
- if (!viewData)
2927
- return [];
2928
- return viewData.values();
3996
+ get connectionState() {
3997
+ return this.connection.getState();
2929
3998
  }
2930
- get(viewPath, key) {
2931
- const viewData = this.views.get(viewPath);
2932
- if (!viewData)
2933
- return null;
2934
- const value = viewData.get(key);
2935
- return value !== undefined ? value : null;
3999
+ get stackName() {
4000
+ return this.stack.name;
2936
4001
  }
2937
- getAllSync(viewPath) {
2938
- const viewData = this.views.get(viewPath);
2939
- if (!viewData)
2940
- return undefined;
2941
- return viewData.values();
4002
+ get store() {
4003
+ return this.storage;
2942
4004
  }
2943
- getSync(viewPath, key) {
2944
- const viewData = this.views.get(viewPath);
2945
- if (!viewData)
2946
- return undefined;
2947
- const value = viewData.get(key);
2948
- return value !== undefined ? value : null;
4005
+ onConnectionStateChange(callback) {
4006
+ return this.connection.onStateChange(callback);
2949
4007
  }
2950
- keys(viewPath) {
2951
- const viewData = this.views.get(viewPath);
2952
- if (!viewData)
2953
- return [];
2954
- return viewData.keys();
4008
+ onFrame(callback) {
4009
+ return this.connection.onFrame(callback);
2955
4010
  }
2956
- size(viewPath) {
2957
- const viewData = this.views.get(viewPath);
2958
- return viewData?.size ?? 0;
4011
+ onSocketIssue(callback) {
4012
+ return this.connection.onSocketIssue(callback);
2959
4013
  }
2960
- clear() {
2961
- this.views.clear();
4014
+ async connect() {
4015
+ await this.connection.connect();
2962
4016
  }
2963
- clearView(viewPath) {
2964
- this.views.delete(viewPath);
2965
- this.viewConfigs.delete(viewPath);
4017
+ disconnect() {
4018
+ this.subscriptionRegistry.clear();
4019
+ this.connection.disconnect();
2966
4020
  }
2967
- getViewConfig(viewPath) {
2968
- return this.viewConfigs.get(viewPath);
4021
+ isConnected() {
4022
+ return this.connection.isConnected();
2969
4023
  }
2970
- setViewConfig(viewPath, config) {
2971
- this.viewConfigs.set(viewPath, config);
2972
- const existingView = this.views.get(viewPath);
2973
- if (existingView && config.sort) {
2974
- existingView.setSortConfig(config.sort);
2975
- }
4024
+ clearStore() {
4025
+ this.storage.clear();
2976
4026
  }
2977
- onUpdate(callback) {
2978
- this.updateCallbacks.add(callback);
2979
- return () => {
2980
- this.updateCallbacks.delete(callback);
2981
- };
4027
+ getStore() {
4028
+ return this.storage;
2982
4029
  }
2983
- onRichUpdate(callback) {
2984
- this.richUpdateCallbacks.add(callback);
2985
- return () => {
2986
- this.richUpdateCallbacks.delete(callback);
2987
- };
4030
+ getConnection() {
4031
+ return this.connection;
2988
4032
  }
2989
- subscribe(viewPath, callback) {
2990
- const handler = (path, _key, update) => {
2991
- if (path === viewPath) {
2992
- callback(update);
2993
- }
2994
- };
2995
- this.updateCallbacks.add(handler);
2996
- return () => {
2997
- this.updateCallbacks.delete(handler);
2998
- };
4033
+ getSubscriptionRegistry() {
4034
+ return this.subscriptionRegistry;
2999
4035
  }
3000
- subscribeToKey(viewPath, key, callback) {
3001
- const handler = (path, updateKey, update) => {
3002
- if (path === viewPath && updateKey === key) {
3003
- callback(update);
4036
+ };
4037
+
4038
+ function createSignerRegistry(entries = []) {
4039
+ const signers = new Map();
4040
+ for (const [address, signer] of entries) {
4041
+ if (!address) {
4042
+ throw new Error('Signer registry addresses must not be empty');
4043
+ }
4044
+ signers.set(address, signer);
4045
+ }
4046
+ return {
4047
+ register(address, signer) {
4048
+ if (!address) {
4049
+ throw new Error('Signer registry addresses must not be empty');
3004
4050
  }
3005
- };
3006
- this.updateCallbacks.add(handler);
3007
- return () => {
3008
- this.updateCallbacks.delete(handler);
3009
- };
4051
+ signers.set(address, signer);
4052
+ },
4053
+ unregister(address) {
4054
+ return signers.delete(address);
4055
+ },
4056
+ get(address) {
4057
+ return signers.get(address);
4058
+ },
4059
+ has(address) {
4060
+ return signers.has(address);
4061
+ },
4062
+ addresses() {
4063
+ return [...signers.keys()];
4064
+ },
4065
+ values() {
4066
+ return [...signers.values()];
4067
+ },
4068
+ entries() {
4069
+ return [...signers.entries()];
4070
+ },
4071
+ clear() {
4072
+ signers.clear();
4073
+ },
4074
+ };
4075
+ }
4076
+
4077
+ function resolveMemberConnectOptions(stack, member, options, forceHttpOnly) {
4078
+ const transport = member?.transport ?? options?.transport ?? (forceHttpOnly ? 'http' : 'ws');
4079
+ // Resolution order: per-member override → the stack's own endpoints →
4080
+ // session-wide fallback endpoints.
4081
+ const url = member?.url ?? (stack.endpoints.ws || options?.endpoints?.ws);
4082
+ const httpUrl = member?.httpUrl ?? stack.endpoints.http ?? options?.endpoints?.http;
4083
+ return {
4084
+ url,
4085
+ httpUrl,
4086
+ transport,
4087
+ auth: member?.auth ?? options?.auth,
4088
+ storage: member?.storage,
4089
+ autoReconnect: member?.autoReconnect,
4090
+ wallet: options?.wallet,
4091
+ fetch: options?.fetch,
4092
+ };
4093
+ }
4094
+ function programAsStack(name, program) {
4095
+ return {
4096
+ name,
4097
+ endpoints: { ws: '' },
4098
+ views: {},
4099
+ programs: { [name]: program },
4100
+ };
4101
+ }
4102
+ async function createSession(definition, options) {
4103
+ const stackEntries = Object.entries(definition.stacks ?? {});
4104
+ const programEntries = Object.entries(definition.programs ?? {});
4105
+ if (stackEntries.length === 0 && programEntries.length === 0) {
4106
+ throw new AreteError('createSession requires at least one stack or program member', 'INVALID_CONFIG');
4107
+ }
4108
+ let wallet = options?.wallet;
4109
+ const signerRegistry = options?.signerRegistry ?? createSignerRegistry();
4110
+ const connectedStacks = await Promise.all(stackEntries.map(async ([key, stack]) => {
4111
+ const memberOptions = options?.stacks?.[key];
4112
+ const effectiveStack = withPrograms(stack, memberOptions?.programs);
4113
+ const connectOptions = resolveMemberConnectOptions(effectiveStack, memberOptions, options, false);
4114
+ const client = await Arete$1.connect(effectiveStack, connectOptions);
4115
+ return [key, client];
4116
+ }));
4117
+ const connectedPrograms = await Promise.all(programEntries.map(async ([key, program]) => {
4118
+ const syntheticStack = programAsStack(key, program);
4119
+ const connectOptions = resolveMemberConnectOptions(syntheticStack, options?.programs?.[key], options, true);
4120
+ const client = await Arete$1.connect(syntheticStack, connectOptions);
4121
+ return [key, client];
4122
+ }));
4123
+ const memberClients = [
4124
+ ...connectedStacks.map(([, client]) => client),
4125
+ ...connectedPrograms.map(([, client]) => client),
4126
+ ];
4127
+ const executionHost = memberClients[0];
4128
+ const stacks = Object.fromEntries(connectedStacks);
4129
+ const explicitProgramKeys = new Set(connectedPrograms.map(([key]) => key));
4130
+ const programOwners = new Map();
4131
+ const connectedProgramEntries = connectedPrograms.map(([key, client]) => [
4132
+ key,
4133
+ client.programs[key],
4134
+ ]);
4135
+ const promotedPrograms = Object.fromEntries(connectedProgramEntries);
4136
+ for (const [stackKey, client] of connectedStacks) {
4137
+ for (const [programKey, program] of Object.entries(client.programs)) {
4138
+ if (explicitProgramKeys.has(programKey)) {
4139
+ continue;
4140
+ }
4141
+ const existingOwner = programOwners.get(programKey);
4142
+ if (existingOwner) {
4143
+ console.warn(`Program '${programKey}' is bundled by stacks '${existingOwner.stack}'` +
4144
+ ` (${existingOwner.programId ?? 'unknown program ID'}) and '${stackKey}'` +
4145
+ ` (${program.programId ?? 'unknown program ID'}); session.programs.${programKey}` +
4146
+ ` uses '${existingOwner.stack}' because it was connected first`);
4147
+ continue;
4148
+ }
4149
+ promotedPrograms[programKey] = program;
4150
+ programOwners.set(programKey, { stack: stackKey, programId: program.programId });
4151
+ }
4152
+ }
4153
+ const programs = promotedPrograms;
4154
+ const chain = options?.chain ?? (options?.endpoints?.http !== undefined
4155
+ ? createChainClient(options.endpoints.http, (options?.fetch ?? globalThis.fetch?.bind(globalThis)))
4156
+ : executionHost.chain);
4157
+ const session = {
4158
+ stacks,
4159
+ programs,
4160
+ get wallet() {
4161
+ return wallet;
4162
+ },
4163
+ signerRegistry,
4164
+ chain,
4165
+ transaction(instructions, transactionOptions) {
4166
+ const defaults = options?.execution;
4167
+ const configuredSigners = transactionOptions?.signers ?? defaults?.signers;
4168
+ const signers = [...new Set([...signerRegistry.values(), ...(configuredSigners ?? [])])];
4169
+ return executionHost.transaction(instructions, {
4170
+ wallet: transactionOptions?.wallet ?? defaults?.wallet,
4171
+ send: defaults?.send || transactionOptions?.send
4172
+ ? { ...(defaults?.send ?? {}), ...(transactionOptions?.send ?? {}) }
4173
+ : undefined,
4174
+ errors: transactionOptions?.errors,
4175
+ signers: signers.length > 0 ? signers : undefined,
4176
+ });
4177
+ },
4178
+ execute(prepared, executionOptions) {
4179
+ const defaults = options?.execution;
4180
+ const configuredSigners = executionOptions?.signers ?? defaults?.signers;
4181
+ return executionHost.execute(prepared, {
4182
+ wallet: executionOptions?.wallet ?? defaults?.wallet,
4183
+ send: defaults?.send || executionOptions?.send
4184
+ ? { ...(defaults?.send ?? {}), ...(executionOptions?.send ?? {}) }
4185
+ : undefined,
4186
+ signers: configuredSigners,
4187
+ signerRegistry,
4188
+ availableSignerAddresses: executionOptions?.availableSignerAddresses ?? defaults?.availableSignerAddresses,
4189
+ onTransactionStart: executionOptions?.onTransactionStart ?? defaults?.onTransactionStart,
4190
+ onTransactionSuccess: executionOptions?.onTransactionSuccess ?? defaults?.onTransactionSuccess,
4191
+ });
4192
+ },
4193
+ setWallet(nextWallet) {
4194
+ wallet = nextWallet;
4195
+ for (const client of memberClients) {
4196
+ client.setWallet(nextWallet);
4197
+ }
4198
+ },
4199
+ close() {
4200
+ for (const client of memberClients) {
4201
+ client.disconnect();
4202
+ }
4203
+ },
4204
+ };
4205
+ return session;
4206
+ }
4207
+
4208
+ function instructionOperation(prepare) {
4209
+ return {
4210
+ kind: 'instruction',
4211
+ prepare: async (input) => prepare(input),
4212
+ };
4213
+ }
4214
+ function transactionOperation(prepare) {
4215
+ return {
4216
+ kind: 'transaction',
4217
+ prepare: async (input) => prepare(input),
4218
+ };
4219
+ }
4220
+ function flowOperation(prepare) {
4221
+ return {
4222
+ kind: 'flow',
4223
+ prepare: async (input) => prepare(input),
4224
+ };
4225
+ }
4226
+
4227
+ function chainAccountLoader(chain) {
4228
+ return {
4229
+ async getAccount(address) {
4230
+ const account = await chain.account(address);
4231
+ return account ? { data: account.data } : null;
4232
+ },
4233
+ };
4234
+ }
4235
+
4236
+ const SPL_TOKEN_PROGRAM_ADDRESS = 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA';
4237
+ const TOKEN_2022_PROGRAM_ADDRESS = 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb';
4238
+ const ASSOCIATED_TOKEN_PROGRAM_ADDRESS = 'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL';
4239
+ const SYSTEM_PROGRAM_ADDRESS = '11111111111111111111111111111111';
4240
+ async function resolveTokenProgramAddress(chain, mint, override) {
4241
+ if (override) {
4242
+ return override;
4243
+ }
4244
+ const mintAccount = await chain.mint(mint);
4245
+ if (!mintAccount) {
4246
+ throw new Error(`Mint account not found while resolving token program: ${mint}`);
4247
+ }
4248
+ if (mintAccount.ownerProgram !== SPL_TOKEN_PROGRAM_ADDRESS &&
4249
+ mintAccount.ownerProgram !== TOKEN_2022_PROGRAM_ADDRESS) {
4250
+ throw new Error(`Mint ${mint} is owned by unsupported token program ${mintAccount.ownerProgram}`);
4251
+ }
4252
+ return mintAccount.ownerProgram;
4253
+ }
4254
+ function deriveAssociatedTokenAccount(input) {
4255
+ const [address] = findProgramAddressSync([
4256
+ createPublicKeySeed(input.owner),
4257
+ createPublicKeySeed(input.tokenProgram ?? SPL_TOKEN_PROGRAM_ADDRESS),
4258
+ createPublicKeySeed(input.mint),
4259
+ ], ASSOCIATED_TOKEN_PROGRAM_ADDRESS);
4260
+ return address;
4261
+ }
4262
+
4263
+ /** Convert a UI amount ("1.5") to raw base units using string math (no float precision loss). */
4264
+ function parseUiAmountToRaw(value, decimals) {
4265
+ const trimmed = String(value).trim();
4266
+ if (!/^\d+(?:\.\d+)?$/.test(trimmed)) {
4267
+ throw new Error(`Invalid UI amount: ${value}`);
4268
+ }
4269
+ const [wholePart, fractionPart = ''] = trimmed.split('.');
4270
+ if (fractionPart.length > decimals) {
4271
+ const excess = fractionPart.slice(decimals);
4272
+ if (/[1-9]/.test(excess)) {
4273
+ throw new Error(`UI amount ${value} has more fractional digits than the mint's ${decimals} decimals`);
4274
+ }
4275
+ }
4276
+ const fraction = fractionPart.padEnd(decimals, '0');
4277
+ const whole = BigInt(wholePart || '0') * 10n ** BigInt(decimals);
4278
+ const fractional = BigInt(fraction.slice(0, decimals) || '0');
4279
+ return whole + fractional;
4280
+ }
4281
+ /** Format raw base units as a UI decimal string (inverse of {@link parseUiAmountToRaw}). */
4282
+ function formatRawToUi(raw, decimals) {
4283
+ const value = BigInt(raw);
4284
+ const negative = value < 0n;
4285
+ const magnitude = negative ? -value : value;
4286
+ const scale = 10n ** BigInt(decimals);
4287
+ const whole = magnitude / scale;
4288
+ const fraction = magnitude % scale;
4289
+ const sign = negative ? '-' : '';
4290
+ if (decimals === 0 || fraction === 0n) {
4291
+ return `${sign}${whole.toString()}`;
4292
+ }
4293
+ const fractionText = fraction.toString().padStart(decimals, '0').replace(/0+$/, '');
4294
+ return `${sign}${whole.toString()}.${fractionText}`;
4295
+ }
4296
+ /** Resolve an {@link AmountInput} to raw base units with known decimals. */
4297
+ function toRawAmount(amount, decimals) {
4298
+ if (typeof amount === 'bigint') {
4299
+ return amount;
3010
4300
  }
3011
- notifyUpdate(viewPath, key, update) {
3012
- for (const callback of this.updateCallbacks) {
3013
- callback(viewPath, key, update);
3014
- }
4301
+ if ('raw' in amount) {
4302
+ return BigInt(amount.raw);
3015
4303
  }
3016
- notifyRichUpdate(viewPath, key, before, after, _op, patch) {
3017
- const richUpdate = before === undefined
3018
- ? { type: 'created', key, data: after }
3019
- : { type: 'updated', key, before, after, patch };
3020
- for (const callback of this.richUpdateCallbacks) {
3021
- callback(viewPath, key, richUpdate);
3022
- }
4304
+ return parseUiAmountToRaw(amount.ui, decimals);
4305
+ }
4306
+ /** Fetch a mint's decimals via the chain read endpoint, throwing when unavailable. */
4307
+ async function getMintDecimals(chain, mint) {
4308
+ const account = await chain.mint(mint);
4309
+ if (!account || account.decimals == null) {
4310
+ throw new Error(`Mint ${mint} is missing decimals on the configured read endpoint.`);
3023
4311
  }
3024
- notifyRichDelete(viewPath, key, lastKnown) {
3025
- const richUpdate = { type: 'deleted', key, lastKnown };
3026
- for (const callback of this.richUpdateCallbacks) {
3027
- callback(viewPath, key, richUpdate);
3028
- }
4312
+ return account.decimals;
4313
+ }
4314
+ /**
4315
+ * Resolve an {@link AmountInput} to raw base units, fetching the mint's
4316
+ * decimals only when they are unknown and actually needed (a bare bigint or
4317
+ * `{raw}` input with explicit `decimals` never touches the network).
4318
+ */
4319
+ async function resolveAmount(chain, input) {
4320
+ const needsDecimalsForParse = typeof input.amount !== 'bigint' && 'ui' in input.amount;
4321
+ if (!needsDecimalsForParse) {
4322
+ const raw = typeof input.amount === 'bigint'
4323
+ ? input.amount
4324
+ : BigInt(input.amount.raw);
4325
+ const decimals = input.decimals ?? await getMintDecimals(chain, input.mint);
4326
+ return { raw, decimals };
4327
+ }
4328
+ const decimals = input.decimals ?? await getMintDecimals(chain, input.mint);
4329
+ return { raw: toRawAmount(input.amount, decimals), decimals };
4330
+ }
4331
+ /**
4332
+ * Resolve an {@link AmountInput} to raw base units without forcing a decimals
4333
+ * fetch when the input is already expressed in raw units.
4334
+ */
4335
+ async function resolveAmountToRaw(chain, input) {
4336
+ if (typeof input.amount === 'bigint') {
4337
+ return input.amount;
4338
+ }
4339
+ if ('raw' in input.amount) {
4340
+ return BigInt(input.amount.raw);
3029
4341
  }
4342
+ const decimals = input.decimals ?? await getMintDecimals(chain, input.mint);
4343
+ return toRawAmount(input.amount, decimals);
3030
4344
  }
4345
+ /** Resolve a named set of {@link AmountInput} values to raw base units. */
4346
+ async function resolveAmountsToRaw(chain, inputs) {
4347
+ const entries = await Promise.all(Object.entries(inputs).map(async ([name, input]) => {
4348
+ const raw = await resolveAmountToRaw(chain, input);
4349
+ return [name, raw];
4350
+ }));
4351
+ return Object.fromEntries(entries);
4352
+ }
4353
+
4354
+ const Arete = Object.assign(Arete$1, { session: createSession });
3031
4355
 
4356
+ exports.ASSOCIATED_TOKEN_PROGRAM_ADDRESS = ASSOCIATED_TOKEN_PROGRAM_ADDRESS;
3032
4357
  exports.Arete = Arete;
3033
4358
  exports.AreteError = AreteError;
3034
4359
  exports.ConnectionManager = ConnectionManager;
3035
4360
  exports.DEFAULT_CONFIG = DEFAULT_CONFIG;
3036
4361
  exports.DEFAULT_MAX_ENTRIES_PER_VIEW = DEFAULT_MAX_ENTRIES_PER_VIEW;
3037
- exports.EntityStore = EntityStore;
3038
4362
  exports.FrameProcessor = FrameProcessor;
4363
+ exports.InstructionError = InstructionError;
3039
4364
  exports.MemoryAdapter = MemoryAdapter;
4365
+ exports.OperationExecutionError = OperationExecutionError;
4366
+ exports.PROGRAM_OPERATION_EXTENSIONS = PROGRAM_OPERATION_EXTENSIONS;
4367
+ exports.ReadRequestError = ReadRequestError;
4368
+ exports.SPL_TOKEN_PROGRAM_ADDRESS = SPL_TOKEN_PROGRAM_ADDRESS;
4369
+ exports.STACK_RUNTIME_EXTENSIONS = STACK_RUNTIME_EXTENSIONS;
4370
+ exports.SYSTEM_PROGRAM_ADDRESS = SYSTEM_PROGRAM_ADDRESS;
3040
4371
  exports.SubscriptionRegistry = SubscriptionRegistry;
4372
+ exports.TOKEN_2022_PROGRAM_ADDRESS = TOKEN_2022_PROGRAM_ADDRESS;
3041
4373
  exports.account = account;
4374
+ exports.appendFlowTransactions = appendFlowTransactions;
4375
+ exports.appendTransactionInstructions = appendTransactionInstructions;
4376
+ exports.applyConnectedStackExtensions = applyConnectedStackExtensions;
3042
4377
  exports.arg = arg;
4378
+ exports.buildInstruction = buildInstruction;
3043
4379
  exports.bytes = bytes;
4380
+ exports.chainAccountLoader = chainAccountLoader;
4381
+ exports.createChainClient = createChainClient;
3044
4382
  exports.createEntityStream = createEntityStream;
3045
4383
  exports.createInstructionExecutor = createInstructionExecutor;
4384
+ exports.createInstructionHandler = createInstructionHandler;
4385
+ exports.createPreparedFlow = createPreparedFlow;
4386
+ exports.createPreparedInstruction = createPreparedInstruction;
4387
+ exports.createPreparedTransaction = createPreparedTransaction;
4388
+ exports.createPreparedTransactionBody = createPreparedTransactionBody;
3046
4389
  exports.createProgramPdas = createProgramPdas;
3047
4390
  exports.createPublicKeySeed = createPublicKeySeed;
3048
4391
  exports.createRichUpdateStream = createRichUpdateStream;
3049
4392
  exports.createSeed = createSeed;
4393
+ exports.createSession = createSession;
4394
+ exports.createSignerRegistry = createSignerRegistry;
3050
4395
  exports.createTypedListView = createTypedListView;
3051
4396
  exports.createTypedStateView = createTypedStateView;
3052
4397
  exports.createTypedViews = createTypedViews;
3053
4398
  exports.createUpdateStream = createUpdateStream;
3054
4399
  exports.decodeBase58 = decodeBase58;
4400
+ exports.defineProgramExtensions = defineProgramExtensions;
4401
+ exports.defineStackExtensions = defineStackExtensions;
4402
+ exports.deriveAssociatedTokenAccount = deriveAssociatedTokenAccount;
4403
+ exports.deriveHttpEndpoint = deriveHttpEndpoint;
3055
4404
  exports.derivePda = findProgramAddress;
4405
+ exports.describePreparedOperation = describePreparedOperation;
3056
4406
  exports.encodeBase58 = encodeBase58;
3057
4407
  exports.executeInstruction = executeInstruction;
4408
+ exports.executePreparedOperation = executePreparedOperation;
4409
+ exports.extendProgram = extendProgram;
4410
+ exports.extendPrograms = extendPrograms;
4411
+ exports.extendStack = extendStack;
3058
4412
  exports.findProgramAddress = findProgramAddress;
3059
4413
  exports.findProgramAddressSync = findProgramAddressSync;
4414
+ exports.flowOperation = flowOperation;
4415
+ exports.formatPreparedOperation = formatPreparedOperation;
3060
4416
  exports.formatProgramError = formatProgramError;
4417
+ exports.formatRawToUi = formatRawToUi;
4418
+ exports.getMintDecimals = getMintDecimals;
4419
+ exports.getProgramRuntimeExtensions = getProgramRuntimeExtensions;
4420
+ exports.getStackRuntimeExtensions = getStackRuntimeExtensions;
4421
+ exports.instructionOperation = instructionOperation;
3061
4422
  exports.isEntityFrame = isEntityFrame;
3062
4423
  exports.isSnapshotFrame = isSnapshotFrame;
3063
4424
  exports.isSubscribedFrame = isSubscribedFrame;
@@ -3066,9 +4427,22 @@ exports.literal = literal;
3066
4427
  exports.parseFrame = parseFrame;
3067
4428
  exports.parseFrameFromBlob = parseFrameFromBlob;
3068
4429
  exports.parseInstructionError = parseInstructionError;
4430
+ exports.parseUiAmountToRaw = parseUiAmountToRaw;
3069
4431
  exports.pda = pda;
4432
+ exports.prependFlowTransactionInstructions = prependFlowTransactionInstructions;
4433
+ exports.prependTransactionInstructions = prependTransactionInstructions;
4434
+ exports.programAccountRead = programAccountRead;
4435
+ exports.programQuery = programQuery;
3070
4436
  exports.resolveAccounts = resolveAccounts;
4437
+ exports.resolveAmount = resolveAmount;
4438
+ exports.resolveAmountToRaw = resolveAmountToRaw;
4439
+ exports.resolveAmountsToRaw = resolveAmountsToRaw;
4440
+ exports.resolveTokenProgramAddress = resolveTokenProgramAddress;
3071
4441
  exports.serializeInstructionData = serializeInstructionData;
4442
+ exports.stackQuery = stackQuery;
4443
+ exports.toJsonValue = toJsonValue;
4444
+ exports.toRawAmount = toRawAmount;
4445
+ exports.transactionOperation = transactionOperation;
3072
4446
  exports.validateAccountResolution = validateAccountResolution;
3073
- exports.waitForConfirmation = waitForConfirmation;
4447
+ exports.withPrograms = withPrograms;
3074
4448
  //# sourceMappingURL=index.js.map