@owney/sdk 0.2.18 → 0.3.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.cjs CHANGED
@@ -578,6 +578,19 @@ async function fetchAgentKeys(apiKey) {
578
578
  }
579
579
 
580
580
  // src/lib/helpers/withdraw-helper.ts
581
+ var import_viem = require("viem");
582
+ function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
583
+ const target = asset.toUpperCase();
584
+ return agents.map((agent) => {
585
+ const agentBalance = aggregated[agent.id];
586
+ const tokenBalance = agentBalance?.tokens.find(
587
+ (t) => t.chainId === chainId && t.asset.toUpperCase() === target
588
+ );
589
+ if (!tokenBalance)
590
+ return { agent, balance: 0n };
591
+ return { agent, balance: (0, import_viem.parseUnits)(tokenBalance.amount, decimals) };
592
+ });
593
+ }
581
594
  function planProportionalShares(balances, requested, totalAvailable) {
582
595
  const plans = balances.map(({ agent, balance }) => ({
583
596
  agent,
@@ -602,8 +615,24 @@ function planProportionalShares(balances, requested, totalAvailable) {
602
615
  }
603
616
  return plans;
604
617
  }
605
- function redistributeShare(plans, fromIndex, amount) {
606
- const candidates = plans.slice(fromIndex + 1).filter((c) => c.balance - c.planned > 0n);
618
+ function planDisabledDrain(disabled, requested) {
619
+ const sorted = [...disabled].filter((d) => d.balance > 0n).sort((a, b) => b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0);
620
+ const plans = [];
621
+ let remaining = requested;
622
+ for (const { agent, balance } of sorted) {
623
+ if (remaining === 0n) {
624
+ plans.push({ agent, balance, planned: 0n });
625
+ continue;
626
+ }
627
+ const take = balance < remaining ? balance : remaining;
628
+ plans.push({ agent, balance, planned: take });
629
+ remaining -= take;
630
+ }
631
+ return { plans, remaining };
632
+ }
633
+ function redistributeShare(plans, fromIndex, amount, candidatePool) {
634
+ const pool = candidatePool ?? plans.slice(fromIndex + 1);
635
+ const candidates = pool.filter((c) => c.balance - c.planned > 0n);
607
636
  const totalHeadroom = candidates.reduce(
608
637
  (s, c) => s + (c.balance - c.planned),
609
638
  0n
@@ -675,10 +704,15 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
675
704
  }
676
705
 
677
706
  // src/client.ts
678
- var import_viem = require("viem");
679
707
  var OwneySDK = class {
680
708
  agents = /* @__PURE__ */ new Map();
681
709
  activeAgents = /* @__PURE__ */ new Set();
710
+ /**
711
+ * Agents whose routing flag is_enabled === false. They remain in
712
+ * `this.agents` and may still be added to `activeAgents` for read +
713
+ * withdraw, but they are excluded from new deposits / activations.
714
+ */
715
+ disabledAgents = /* @__PURE__ */ new Set();
682
716
  state = null;
683
717
  apiKey;
684
718
  zyfaiRpcUrls;
@@ -715,8 +749,12 @@ var OwneySDK = class {
715
749
  await agent.disconnect();
716
750
  }
717
751
  this.activeAgents.clear();
752
+ this.disabledAgents.clear();
718
753
  this.state = null;
719
754
  }
755
+ isAgentDisabled(agentId) {
756
+ return this.disabledAgents.has(agentId);
757
+ }
720
758
  /**
721
759
  * Check if the SDK has an active connection state.
722
760
  * @returns `true` if `connect()` has been called and `disconnect()` has not
@@ -768,11 +806,15 @@ var OwneySDK = class {
768
806
  if (!this.initializingAgentsPromise) {
769
807
  this.initializingAgentsPromise = (async () => {
770
808
  const agentKeys = await fetchAgentKeys(this.apiKey);
771
- for (const { key, agent_type } of agentKeys) {
809
+ this.disabledAgents.clear();
810
+ for (const { key, agent_type, is_enabled } of agentKeys) {
772
811
  const agent = this.createAgent(agent_type, key);
773
812
  if (!agent)
774
813
  continue;
775
814
  this.agents.set(agent_type, agent);
815
+ if (is_enabled === false) {
816
+ this.disabledAgents.add(agent_type);
817
+ }
776
818
  }
777
819
  if (this.agents.size === 0) {
778
820
  throw new OwneyError(
@@ -817,6 +859,16 @@ var OwneySDK = class {
817
859
  }
818
860
  const uniqueIds = [...new Set(agentId)];
819
861
  const agents = uniqueIds.map((id) => this.getAgent(id));
862
+ const disabledRequested = uniqueIds.filter(
863
+ (id) => this.isAgentDisabled(id)
864
+ );
865
+ if (disabledRequested.length > 0) {
866
+ throw new OwneyError(
867
+ "AGENT_DISABLED",
868
+ `Cannot activate disabled agent(s): ${disabledRequested.join(", ")}.`,
869
+ { agentIds: disabledRequested }
870
+ );
871
+ }
820
872
  const incompatibleAgents = agents.filter(
821
873
  (agent) => !agent.supportedChainIds.includes(chainId)
822
874
  ).map((agent) => ({
@@ -855,8 +907,11 @@ var OwneySDK = class {
855
907
  this.activeAgents.add(agent.id);
856
908
  }
857
909
  state.chainId = chainId;
910
+ const enabledCompatible = compatible.filter(
911
+ (agent) => !this.isAgentDisabled(agent.id)
912
+ );
858
913
  Promise.all(
859
- compatible.map(async (agent) => {
914
+ enabledCompatible.map(async (agent) => {
860
915
  await agent.activateAgent(state, chainId);
861
916
  })
862
917
  ).catch((error) => {
@@ -883,13 +938,22 @@ var OwneySDK = class {
883
938
  const state = this.requireState();
884
939
  const chainId = this.requireChainId();
885
940
  if (agentId) {
941
+ if (this.isAgentDisabled(agentId)) {
942
+ throw new OwneyError(
943
+ "AGENT_DISABLED",
944
+ `Cannot deposit into disabled agent "${agentId}". Existing balances remain visible and withdrawable.`,
945
+ { agentId }
946
+ );
947
+ }
886
948
  const agent = this.getAgent(agentId);
887
949
  this.validateAssetSupport(agent, chainId, asset);
888
950
  this.validateMinDepositAmount(agent, chainId, asset, amount);
889
951
  return agent.deposit(state, chainId, amount, depositCallback);
890
952
  }
891
953
  const totalAmount = BigInt(amount);
892
- const eligibleAgents = this.getEligibleAgents(chainId, asset);
954
+ const eligibleAgents = this.getEligibleAgents(chainId, asset, {
955
+ excludeDisabled: true
956
+ });
893
957
  const agentAmounts = this.splitDepositAmount(
894
958
  totalAmount,
895
959
  eligibleAgents,
@@ -973,8 +1037,11 @@ var OwneySDK = class {
973
1037
  );
974
1038
  }
975
1039
  }
976
- getEligibleAgents(chainId, asset) {
1040
+ getEligibleAgents(chainId, asset, opts) {
977
1041
  const eligible = [...this.agents.values()].filter((agent) => {
1042
+ if (opts?.excludeDisabled && this.isAgentDisabled(agent.id)) {
1043
+ return false;
1044
+ }
978
1045
  const chainAssets = agent.supportedAssets.find(
979
1046
  (sa) => sa.chainId === chainId
980
1047
  );
@@ -1041,16 +1108,13 @@ var OwneySDK = class {
1041
1108
  }
1042
1109
  const requested = BigInt(amount);
1043
1110
  const aggregated = await this.getBalances();
1044
- const balances = eligibleAgents.map((agent) => {
1045
- const agentBalance = aggregated.agentBalances[agent.id];
1046
- const tokenBalance = agentBalance?.tokens.find(
1047
- (t) => t.chainId === chainId && t.asset.toUpperCase() === asset.toUpperCase()
1048
- );
1049
- if (!tokenBalance)
1050
- return { agent, balance: 0n };
1051
- const balance = (0, import_viem.parseUnits)(tokenBalance.amount, assetInfo.decimals);
1052
- return { agent, balance };
1053
- });
1111
+ const balances = projectAgentBalancesForAsset(
1112
+ eligibleAgents,
1113
+ aggregated.agentBalances,
1114
+ chainId,
1115
+ asset,
1116
+ assetInfo.decimals
1117
+ );
1054
1118
  const totalAvailable = balances.reduce((sum, x) => sum + x.balance, 0n);
1055
1119
  if (totalAvailable < requested) {
1056
1120
  throw new OwneyError(
@@ -1063,7 +1127,26 @@ var OwneySDK = class {
1063
1127
  }
1064
1128
  );
1065
1129
  }
1066
- const plans = planProportionalShares(balances, requested, totalAvailable);
1130
+ const disabledBalances = balances.filter(
1131
+ (b) => this.isAgentDisabled(b.agent.id)
1132
+ );
1133
+ const enabledBalances = balances.filter(
1134
+ (b) => !this.isAgentDisabled(b.agent.id)
1135
+ );
1136
+ const { plans: disabledPlans, remaining: afterDrain } = planDisabledDrain(
1137
+ disabledBalances,
1138
+ requested
1139
+ );
1140
+ const enabledTotal = enabledBalances.reduce(
1141
+ (s, b) => s + b.balance,
1142
+ 0n
1143
+ );
1144
+ const enabledPlans = afterDrain > 0n ? planProportionalShares(enabledBalances, afterDrain, enabledTotal) : enabledBalances.map(({ agent, balance }) => ({
1145
+ agent,
1146
+ balance,
1147
+ planned: 0n
1148
+ }));
1149
+ const plans = [...disabledPlans, ...enabledPlans];
1067
1150
  const results = {};
1068
1151
  const agentErrors = {};
1069
1152
  for (let i = 0; i < plans.length; i++) {
@@ -1080,7 +1163,9 @@ var OwneySDK = class {
1080
1163
  } catch (err) {
1081
1164
  console.error(`withdraw failed for agent "${p.agent.id}":`, err);
1082
1165
  agentErrors[p.agent.id] = err instanceof Error ? err.message : String(err);
1083
- redistributeShare(plans, i, p.planned);
1166
+ const failedAmount = p.planned;
1167
+ p.planned = 0n;
1168
+ redistributeShare(plans, i, failedAmount);
1084
1169
  }
1085
1170
  }
1086
1171
  if (Object.keys(results).length === 0) {
package/dist/index.d.cts CHANGED
@@ -211,6 +211,12 @@ interface OwneyAgentAllocation {
211
211
  declare class OwneySDK {
212
212
  private agents;
213
213
  private activeAgents;
214
+ /**
215
+ * Agents whose routing flag is_enabled === false. They remain in
216
+ * `this.agents` and may still be added to `activeAgents` for read +
217
+ * withdraw, but they are excluded from new deposits / activations.
218
+ */
219
+ private disabledAgents;
214
220
  private state;
215
221
  private apiKey;
216
222
  private zyfaiRpcUrls?;
@@ -227,6 +233,7 @@ declare class OwneySDK {
227
233
  * After calling this, `connect()` must be called again before using agent methods.
228
234
  */
229
235
  disconnect(): Promise<void>;
236
+ private isAgentDisabled;
230
237
  /**
231
238
  * Check if the SDK has an active connection state.
232
239
  * @returns `true` if `connect()` has been called and `disconnect()` has not
@@ -349,7 +356,7 @@ declare class OwneySDK {
349
356
  getAgentAllocation(): Promise<OwneyAgentAllocation>;
350
357
  }
351
358
 
352
- type OwneyErrorCode = "NOT_CONNECTED" | "NO_ACTIVE_CHAIN" | "WALLET_NO_ACCOUNTS" | "WALLET_ADDRESS_REQUIRED" | "AGENT_NOT_FOUND" | "AGENT_CHAIN_INCOMPATIBLE" | "AGENT_EMPTY_LIST" | "CHAIN_UNSUPPORTED" | "CHAIN_NO_COMPATIBLE_AGENTS" | "ASSET_UNSUPPORTED" | "ASSET_NO_COMPATIBLE_AGENTS" | "DEPOSIT_AMOUNT_BELOW_MINIMUM" | "DEPOSIT_CALLBACK_REQUIRED" | "DEPOSIT_CALLBACK_INVALID" | "DEPOSIT_NO_PERMITTED_TOKENS" | "WITHDRAW_NO_PERMITTED_TOKENS" | "WITHDRAW_INSUFFICIENT_BALANCE" | "WITHDRAW_ALL_FAILED" | "WITHDRAW_PARTIAL_FAILURE" | "WITHDRAW_FAILED" | "API_ROUTING_ERROR" | "API_ROUTING_FAILED" | "API_SAIL_ERROR" | "API_SAIL_TIMEOUT" | "API_NO_AGENTS" | "BALANCE_ALL_FAILED" | "ALLOCATION_ALL_FAILED" | "VALIDATION_INVALID_DAYS";
359
+ type OwneyErrorCode = "NOT_CONNECTED" | "NO_ACTIVE_CHAIN" | "WALLET_NO_ACCOUNTS" | "WALLET_ADDRESS_REQUIRED" | "AGENT_NOT_FOUND" | "AGENT_CHAIN_INCOMPATIBLE" | "AGENT_EMPTY_LIST" | "AGENT_DISABLED" | "CHAIN_UNSUPPORTED" | "CHAIN_NO_COMPATIBLE_AGENTS" | "ASSET_UNSUPPORTED" | "ASSET_NO_COMPATIBLE_AGENTS" | "DEPOSIT_AMOUNT_BELOW_MINIMUM" | "DEPOSIT_CALLBACK_REQUIRED" | "DEPOSIT_CALLBACK_INVALID" | "DEPOSIT_NO_PERMITTED_TOKENS" | "WITHDRAW_NO_PERMITTED_TOKENS" | "WITHDRAW_INSUFFICIENT_BALANCE" | "WITHDRAW_ALL_FAILED" | "WITHDRAW_PARTIAL_FAILURE" | "WITHDRAW_FAILED" | "API_ROUTING_ERROR" | "API_ROUTING_FAILED" | "API_SAIL_ERROR" | "API_SAIL_TIMEOUT" | "API_NO_AGENTS" | "BALANCE_ALL_FAILED" | "ALLOCATION_ALL_FAILED" | "VALIDATION_INVALID_DAYS";
353
360
  declare class OwneyError extends Error {
354
361
  readonly code: OwneyErrorCode;
355
362
  readonly details?: Record<string, unknown>;
package/dist/index.d.ts CHANGED
@@ -211,6 +211,12 @@ interface OwneyAgentAllocation {
211
211
  declare class OwneySDK {
212
212
  private agents;
213
213
  private activeAgents;
214
+ /**
215
+ * Agents whose routing flag is_enabled === false. They remain in
216
+ * `this.agents` and may still be added to `activeAgents` for read +
217
+ * withdraw, but they are excluded from new deposits / activations.
218
+ */
219
+ private disabledAgents;
214
220
  private state;
215
221
  private apiKey;
216
222
  private zyfaiRpcUrls?;
@@ -227,6 +233,7 @@ declare class OwneySDK {
227
233
  * After calling this, `connect()` must be called again before using agent methods.
228
234
  */
229
235
  disconnect(): Promise<void>;
236
+ private isAgentDisabled;
230
237
  /**
231
238
  * Check if the SDK has an active connection state.
232
239
  * @returns `true` if `connect()` has been called and `disconnect()` has not
@@ -349,7 +356,7 @@ declare class OwneySDK {
349
356
  getAgentAllocation(): Promise<OwneyAgentAllocation>;
350
357
  }
351
358
 
352
- type OwneyErrorCode = "NOT_CONNECTED" | "NO_ACTIVE_CHAIN" | "WALLET_NO_ACCOUNTS" | "WALLET_ADDRESS_REQUIRED" | "AGENT_NOT_FOUND" | "AGENT_CHAIN_INCOMPATIBLE" | "AGENT_EMPTY_LIST" | "CHAIN_UNSUPPORTED" | "CHAIN_NO_COMPATIBLE_AGENTS" | "ASSET_UNSUPPORTED" | "ASSET_NO_COMPATIBLE_AGENTS" | "DEPOSIT_AMOUNT_BELOW_MINIMUM" | "DEPOSIT_CALLBACK_REQUIRED" | "DEPOSIT_CALLBACK_INVALID" | "DEPOSIT_NO_PERMITTED_TOKENS" | "WITHDRAW_NO_PERMITTED_TOKENS" | "WITHDRAW_INSUFFICIENT_BALANCE" | "WITHDRAW_ALL_FAILED" | "WITHDRAW_PARTIAL_FAILURE" | "WITHDRAW_FAILED" | "API_ROUTING_ERROR" | "API_ROUTING_FAILED" | "API_SAIL_ERROR" | "API_SAIL_TIMEOUT" | "API_NO_AGENTS" | "BALANCE_ALL_FAILED" | "ALLOCATION_ALL_FAILED" | "VALIDATION_INVALID_DAYS";
359
+ type OwneyErrorCode = "NOT_CONNECTED" | "NO_ACTIVE_CHAIN" | "WALLET_NO_ACCOUNTS" | "WALLET_ADDRESS_REQUIRED" | "AGENT_NOT_FOUND" | "AGENT_CHAIN_INCOMPATIBLE" | "AGENT_EMPTY_LIST" | "AGENT_DISABLED" | "CHAIN_UNSUPPORTED" | "CHAIN_NO_COMPATIBLE_AGENTS" | "ASSET_UNSUPPORTED" | "ASSET_NO_COMPATIBLE_AGENTS" | "DEPOSIT_AMOUNT_BELOW_MINIMUM" | "DEPOSIT_CALLBACK_REQUIRED" | "DEPOSIT_CALLBACK_INVALID" | "DEPOSIT_NO_PERMITTED_TOKENS" | "WITHDRAW_NO_PERMITTED_TOKENS" | "WITHDRAW_INSUFFICIENT_BALANCE" | "WITHDRAW_ALL_FAILED" | "WITHDRAW_PARTIAL_FAILURE" | "WITHDRAW_FAILED" | "API_ROUTING_ERROR" | "API_ROUTING_FAILED" | "API_SAIL_ERROR" | "API_SAIL_TIMEOUT" | "API_NO_AGENTS" | "BALANCE_ALL_FAILED" | "ALLOCATION_ALL_FAILED" | "VALIDATION_INVALID_DAYS";
353
360
  declare class OwneyError extends Error {
354
361
  readonly code: OwneyErrorCode;
355
362
  readonly details?: Record<string, unknown>;
package/dist/index.js CHANGED
@@ -548,6 +548,19 @@ async function fetchAgentKeys(apiKey) {
548
548
  }
549
549
 
550
550
  // src/lib/helpers/withdraw-helper.ts
551
+ import { parseUnits } from "viem";
552
+ function projectAgentBalancesForAsset(agents, aggregated, chainId, asset, decimals) {
553
+ const target = asset.toUpperCase();
554
+ return agents.map((agent) => {
555
+ const agentBalance = aggregated[agent.id];
556
+ const tokenBalance = agentBalance?.tokens.find(
557
+ (t) => t.chainId === chainId && t.asset.toUpperCase() === target
558
+ );
559
+ if (!tokenBalance)
560
+ return { agent, balance: 0n };
561
+ return { agent, balance: parseUnits(tokenBalance.amount, decimals) };
562
+ });
563
+ }
551
564
  function planProportionalShares(balances, requested, totalAvailable) {
552
565
  const plans = balances.map(({ agent, balance }) => ({
553
566
  agent,
@@ -572,8 +585,24 @@ function planProportionalShares(balances, requested, totalAvailable) {
572
585
  }
573
586
  return plans;
574
587
  }
575
- function redistributeShare(plans, fromIndex, amount) {
576
- const candidates = plans.slice(fromIndex + 1).filter((c) => c.balance - c.planned > 0n);
588
+ function planDisabledDrain(disabled, requested) {
589
+ const sorted = [...disabled].filter((d) => d.balance > 0n).sort((a, b) => b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0);
590
+ const plans = [];
591
+ let remaining = requested;
592
+ for (const { agent, balance } of sorted) {
593
+ if (remaining === 0n) {
594
+ plans.push({ agent, balance, planned: 0n });
595
+ continue;
596
+ }
597
+ const take = balance < remaining ? balance : remaining;
598
+ plans.push({ agent, balance, planned: take });
599
+ remaining -= take;
600
+ }
601
+ return { plans, remaining };
602
+ }
603
+ function redistributeShare(plans, fromIndex, amount, candidatePool) {
604
+ const pool = candidatePool ?? plans.slice(fromIndex + 1);
605
+ const candidates = pool.filter((c) => c.balance - c.planned > 0n);
577
606
  const totalHeadroom = candidates.reduce(
578
607
  (s, c) => s + (c.balance - c.planned),
579
608
  0n
@@ -645,10 +674,15 @@ function aggregateApyByChainAndAsset(agentApys, agentBalances) {
645
674
  }
646
675
 
647
676
  // src/client.ts
648
- import { parseUnits } from "viem";
649
677
  var OwneySDK = class {
650
678
  agents = /* @__PURE__ */ new Map();
651
679
  activeAgents = /* @__PURE__ */ new Set();
680
+ /**
681
+ * Agents whose routing flag is_enabled === false. They remain in
682
+ * `this.agents` and may still be added to `activeAgents` for read +
683
+ * withdraw, but they are excluded from new deposits / activations.
684
+ */
685
+ disabledAgents = /* @__PURE__ */ new Set();
652
686
  state = null;
653
687
  apiKey;
654
688
  zyfaiRpcUrls;
@@ -685,8 +719,12 @@ var OwneySDK = class {
685
719
  await agent.disconnect();
686
720
  }
687
721
  this.activeAgents.clear();
722
+ this.disabledAgents.clear();
688
723
  this.state = null;
689
724
  }
725
+ isAgentDisabled(agentId) {
726
+ return this.disabledAgents.has(agentId);
727
+ }
690
728
  /**
691
729
  * Check if the SDK has an active connection state.
692
730
  * @returns `true` if `connect()` has been called and `disconnect()` has not
@@ -738,11 +776,15 @@ var OwneySDK = class {
738
776
  if (!this.initializingAgentsPromise) {
739
777
  this.initializingAgentsPromise = (async () => {
740
778
  const agentKeys = await fetchAgentKeys(this.apiKey);
741
- for (const { key, agent_type } of agentKeys) {
779
+ this.disabledAgents.clear();
780
+ for (const { key, agent_type, is_enabled } of agentKeys) {
742
781
  const agent = this.createAgent(agent_type, key);
743
782
  if (!agent)
744
783
  continue;
745
784
  this.agents.set(agent_type, agent);
785
+ if (is_enabled === false) {
786
+ this.disabledAgents.add(agent_type);
787
+ }
746
788
  }
747
789
  if (this.agents.size === 0) {
748
790
  throw new OwneyError(
@@ -787,6 +829,16 @@ var OwneySDK = class {
787
829
  }
788
830
  const uniqueIds = [...new Set(agentId)];
789
831
  const agents = uniqueIds.map((id) => this.getAgent(id));
832
+ const disabledRequested = uniqueIds.filter(
833
+ (id) => this.isAgentDisabled(id)
834
+ );
835
+ if (disabledRequested.length > 0) {
836
+ throw new OwneyError(
837
+ "AGENT_DISABLED",
838
+ `Cannot activate disabled agent(s): ${disabledRequested.join(", ")}.`,
839
+ { agentIds: disabledRequested }
840
+ );
841
+ }
790
842
  const incompatibleAgents = agents.filter(
791
843
  (agent) => !agent.supportedChainIds.includes(chainId)
792
844
  ).map((agent) => ({
@@ -825,8 +877,11 @@ var OwneySDK = class {
825
877
  this.activeAgents.add(agent.id);
826
878
  }
827
879
  state.chainId = chainId;
880
+ const enabledCompatible = compatible.filter(
881
+ (agent) => !this.isAgentDisabled(agent.id)
882
+ );
828
883
  Promise.all(
829
- compatible.map(async (agent) => {
884
+ enabledCompatible.map(async (agent) => {
830
885
  await agent.activateAgent(state, chainId);
831
886
  })
832
887
  ).catch((error) => {
@@ -853,13 +908,22 @@ var OwneySDK = class {
853
908
  const state = this.requireState();
854
909
  const chainId = this.requireChainId();
855
910
  if (agentId) {
911
+ if (this.isAgentDisabled(agentId)) {
912
+ throw new OwneyError(
913
+ "AGENT_DISABLED",
914
+ `Cannot deposit into disabled agent "${agentId}". Existing balances remain visible and withdrawable.`,
915
+ { agentId }
916
+ );
917
+ }
856
918
  const agent = this.getAgent(agentId);
857
919
  this.validateAssetSupport(agent, chainId, asset);
858
920
  this.validateMinDepositAmount(agent, chainId, asset, amount);
859
921
  return agent.deposit(state, chainId, amount, depositCallback);
860
922
  }
861
923
  const totalAmount = BigInt(amount);
862
- const eligibleAgents = this.getEligibleAgents(chainId, asset);
924
+ const eligibleAgents = this.getEligibleAgents(chainId, asset, {
925
+ excludeDisabled: true
926
+ });
863
927
  const agentAmounts = this.splitDepositAmount(
864
928
  totalAmount,
865
929
  eligibleAgents,
@@ -943,8 +1007,11 @@ var OwneySDK = class {
943
1007
  );
944
1008
  }
945
1009
  }
946
- getEligibleAgents(chainId, asset) {
1010
+ getEligibleAgents(chainId, asset, opts) {
947
1011
  const eligible = [...this.agents.values()].filter((agent) => {
1012
+ if (opts?.excludeDisabled && this.isAgentDisabled(agent.id)) {
1013
+ return false;
1014
+ }
948
1015
  const chainAssets = agent.supportedAssets.find(
949
1016
  (sa) => sa.chainId === chainId
950
1017
  );
@@ -1011,16 +1078,13 @@ var OwneySDK = class {
1011
1078
  }
1012
1079
  const requested = BigInt(amount);
1013
1080
  const aggregated = await this.getBalances();
1014
- const balances = eligibleAgents.map((agent) => {
1015
- const agentBalance = aggregated.agentBalances[agent.id];
1016
- const tokenBalance = agentBalance?.tokens.find(
1017
- (t) => t.chainId === chainId && t.asset.toUpperCase() === asset.toUpperCase()
1018
- );
1019
- if (!tokenBalance)
1020
- return { agent, balance: 0n };
1021
- const balance = parseUnits(tokenBalance.amount, assetInfo.decimals);
1022
- return { agent, balance };
1023
- });
1081
+ const balances = projectAgentBalancesForAsset(
1082
+ eligibleAgents,
1083
+ aggregated.agentBalances,
1084
+ chainId,
1085
+ asset,
1086
+ assetInfo.decimals
1087
+ );
1024
1088
  const totalAvailable = balances.reduce((sum, x) => sum + x.balance, 0n);
1025
1089
  if (totalAvailable < requested) {
1026
1090
  throw new OwneyError(
@@ -1033,7 +1097,26 @@ var OwneySDK = class {
1033
1097
  }
1034
1098
  );
1035
1099
  }
1036
- const plans = planProportionalShares(balances, requested, totalAvailable);
1100
+ const disabledBalances = balances.filter(
1101
+ (b) => this.isAgentDisabled(b.agent.id)
1102
+ );
1103
+ const enabledBalances = balances.filter(
1104
+ (b) => !this.isAgentDisabled(b.agent.id)
1105
+ );
1106
+ const { plans: disabledPlans, remaining: afterDrain } = planDisabledDrain(
1107
+ disabledBalances,
1108
+ requested
1109
+ );
1110
+ const enabledTotal = enabledBalances.reduce(
1111
+ (s, b) => s + b.balance,
1112
+ 0n
1113
+ );
1114
+ const enabledPlans = afterDrain > 0n ? planProportionalShares(enabledBalances, afterDrain, enabledTotal) : enabledBalances.map(({ agent, balance }) => ({
1115
+ agent,
1116
+ balance,
1117
+ planned: 0n
1118
+ }));
1119
+ const plans = [...disabledPlans, ...enabledPlans];
1037
1120
  const results = {};
1038
1121
  const agentErrors = {};
1039
1122
  for (let i = 0; i < plans.length; i++) {
@@ -1050,7 +1133,9 @@ var OwneySDK = class {
1050
1133
  } catch (err) {
1051
1134
  console.error(`withdraw failed for agent "${p.agent.id}":`, err);
1052
1135
  agentErrors[p.agent.id] = err instanceof Error ? err.message : String(err);
1053
- redistributeShare(plans, i, p.planned);
1136
+ const failedAmount = p.planned;
1137
+ p.planned = 0n;
1138
+ redistributeShare(plans, i, failedAmount);
1054
1139
  }
1055
1140
  }
1056
1141
  if (Object.keys(results).length === 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owney/sdk",
3
- "version": "0.2.18",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",
@@ -17,7 +17,9 @@
17
17
  ],
18
18
  "scripts": {
19
19
  "build": "tsup",
20
- "dev": "tsup --watch"
20
+ "dev": "tsup --watch",
21
+ "test": "vitest run",
22
+ "test:watch": "vitest"
21
23
  },
22
24
  "dependencies": {
23
25
  "@zyfai/sdk": "^0.2.34",
@@ -26,7 +28,8 @@
26
28
  "devDependencies": {
27
29
  "dotenv": "^17.3.1",
28
30
  "tsup": "^7.0.0",
29
- "typescript": "^5.0.0"
31
+ "typescript": "^5.0.0",
32
+ "vitest": "^4.1.5"
30
33
  },
31
34
  "publishConfig": {
32
35
  "access": "public"