@0xslots/sdk 0.5.0 → 0.6.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/README.md +44 -167
- package/dist/index.d.ts +530 -27
- package/dist/index.js +375 -9
- package/dist/index.js.map +1 -1
- package/package.json +7 -3
package/dist/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { gql as gql$1, GraphQLClient } from 'graphql-request';
|
|
2
2
|
import gql from 'graphql-tag';
|
|
3
|
+
import { encodeFunctionData, erc20Abi } from 'viem';
|
|
4
|
+
import { getSlotsHubAddress, slotAbi, slotFactoryAbi } from '@0xslots/contracts';
|
|
3
5
|
|
|
4
6
|
// src/client.ts
|
|
5
7
|
var AccountFieldsFragmentDoc = gql`
|
|
@@ -32,7 +34,12 @@ var SlotFieldsFragmentDoc = gql`
|
|
|
32
34
|
mutableTax
|
|
33
35
|
mutableModule
|
|
34
36
|
taxPercentage
|
|
35
|
-
module
|
|
37
|
+
module {
|
|
38
|
+
id
|
|
39
|
+
verified
|
|
40
|
+
name
|
|
41
|
+
version
|
|
42
|
+
}
|
|
36
43
|
occupant
|
|
37
44
|
occupantAccount {
|
|
38
45
|
id
|
|
@@ -147,6 +154,40 @@ var GetRecentEventsDocument = gql`
|
|
|
147
154
|
timestamp
|
|
148
155
|
tx
|
|
149
156
|
}
|
|
157
|
+
taxUpdateProposedEvents(first: $first, orderBy: timestamp, orderDirection: desc) {
|
|
158
|
+
id
|
|
159
|
+
slot {
|
|
160
|
+
id
|
|
161
|
+
}
|
|
162
|
+
newPercentage
|
|
163
|
+
timestamp
|
|
164
|
+
tx
|
|
165
|
+
}
|
|
166
|
+
moduleUpdateProposedEvents(
|
|
167
|
+
first: $first
|
|
168
|
+
orderBy: timestamp
|
|
169
|
+
orderDirection: desc
|
|
170
|
+
) {
|
|
171
|
+
id
|
|
172
|
+
slot {
|
|
173
|
+
id
|
|
174
|
+
}
|
|
175
|
+
newModule
|
|
176
|
+
timestamp
|
|
177
|
+
tx
|
|
178
|
+
}
|
|
179
|
+
pendingUpdateCancelledEvents(
|
|
180
|
+
first: $first
|
|
181
|
+
orderBy: timestamp
|
|
182
|
+
orderDirection: desc
|
|
183
|
+
) {
|
|
184
|
+
id
|
|
185
|
+
slot {
|
|
186
|
+
id
|
|
187
|
+
}
|
|
188
|
+
timestamp
|
|
189
|
+
tx
|
|
190
|
+
}
|
|
150
191
|
}
|
|
151
192
|
`;
|
|
152
193
|
var GetBoughtEventsDocument = gql`
|
|
@@ -420,6 +461,38 @@ var GetSlotActivityDocument = gql`
|
|
|
420
461
|
timestamp
|
|
421
462
|
tx
|
|
422
463
|
}
|
|
464
|
+
taxUpdateProposedEvents(
|
|
465
|
+
first: $first
|
|
466
|
+
orderBy: timestamp
|
|
467
|
+
orderDirection: desc
|
|
468
|
+
where: {slot: $slotId}
|
|
469
|
+
) {
|
|
470
|
+
id
|
|
471
|
+
newPercentage
|
|
472
|
+
timestamp
|
|
473
|
+
tx
|
|
474
|
+
}
|
|
475
|
+
moduleUpdateProposedEvents(
|
|
476
|
+
first: $first
|
|
477
|
+
orderBy: timestamp
|
|
478
|
+
orderDirection: desc
|
|
479
|
+
where: {slot: $slotId}
|
|
480
|
+
) {
|
|
481
|
+
id
|
|
482
|
+
newModule
|
|
483
|
+
timestamp
|
|
484
|
+
tx
|
|
485
|
+
}
|
|
486
|
+
pendingUpdateCancelledEvents(
|
|
487
|
+
first: $first
|
|
488
|
+
orderBy: timestamp
|
|
489
|
+
orderDirection: desc
|
|
490
|
+
where: {slot: $slotId}
|
|
491
|
+
) {
|
|
492
|
+
id
|
|
493
|
+
timestamp
|
|
494
|
+
tx
|
|
495
|
+
}
|
|
423
496
|
}
|
|
424
497
|
`;
|
|
425
498
|
var GetFactoryDocument = gql`
|
|
@@ -548,8 +621,6 @@ function getSdk(client, withWrapper = defaultWrapper) {
|
|
|
548
621
|
}
|
|
549
622
|
};
|
|
550
623
|
}
|
|
551
|
-
|
|
552
|
-
// src/client.ts
|
|
553
624
|
var META_QUERY = gql$1`
|
|
554
625
|
query GetMeta {
|
|
555
626
|
_meta {
|
|
@@ -568,26 +639,50 @@ var SlotsChain = /* @__PURE__ */ ((SlotsChain2) => {
|
|
|
568
639
|
return SlotsChain2;
|
|
569
640
|
})(SlotsChain || {});
|
|
570
641
|
var SUBGRAPH_URLS = {
|
|
571
|
-
[84532 /* BASE_SEPOLIA */]: "https://api.studio.thegraph.com/query/958/0-x-slots-base-sepolia/
|
|
642
|
+
[84532 /* BASE_SEPOLIA */]: "https://api.studio.thegraph.com/query/958/0-x-slots-base-sepolia/version/latest",
|
|
572
643
|
[42161 /* ARBITRUM */]: "https://api.studio.thegraph.com/query/958/0-x-slots-arb/version/latest"
|
|
573
644
|
};
|
|
574
645
|
var SlotsClient = class {
|
|
575
646
|
constructor(config) {
|
|
576
647
|
this.chainId = config.chainId;
|
|
648
|
+
this._publicClient = config.publicClient;
|
|
649
|
+
this.walletClient = config.walletClient;
|
|
650
|
+
this._factory = config.factoryAddress ?? getSlotsHubAddress(config.chainId);
|
|
577
651
|
const url = config.subgraphUrl || SUBGRAPH_URLS[config.chainId];
|
|
578
652
|
if (!url) throw new Error(`No subgraph URL for chain ${config.chainId}`);
|
|
579
|
-
this.
|
|
580
|
-
this.sdk = getSdk(this.
|
|
653
|
+
this.gqlClient = new GraphQLClient(url, { headers: config.headers });
|
|
654
|
+
this.sdk = getSdk(this.gqlClient);
|
|
581
655
|
}
|
|
656
|
+
// ─── Accessors ──────────────────────────────────────────────────────────────
|
|
582
657
|
getChainId() {
|
|
583
658
|
return this.chainId;
|
|
584
659
|
}
|
|
585
660
|
getClient() {
|
|
586
|
-
return this.
|
|
661
|
+
return this.gqlClient;
|
|
587
662
|
}
|
|
588
663
|
getSdk() {
|
|
589
664
|
return this.sdk;
|
|
590
665
|
}
|
|
666
|
+
get publicClient() {
|
|
667
|
+
if (!this._publicClient) throw new Error("No publicClient provided");
|
|
668
|
+
return this._publicClient;
|
|
669
|
+
}
|
|
670
|
+
get factory() {
|
|
671
|
+
if (!this._factory) throw new Error("No factoryAddress provided");
|
|
672
|
+
return this._factory;
|
|
673
|
+
}
|
|
674
|
+
get wallet() {
|
|
675
|
+
if (!this.walletClient) throw new Error("No walletClient provided");
|
|
676
|
+
return this.walletClient;
|
|
677
|
+
}
|
|
678
|
+
get account() {
|
|
679
|
+
const account = this.wallet.account;
|
|
680
|
+
if (!account) throw new Error("WalletClient must have an account");
|
|
681
|
+
return account.address;
|
|
682
|
+
}
|
|
683
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
684
|
+
// READ — Subgraph Queries
|
|
685
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
591
686
|
// Slot queries
|
|
592
687
|
getSlots(...args) {
|
|
593
688
|
return this.sdk.GetSlots(...args);
|
|
@@ -631,7 +726,7 @@ var SlotsClient = class {
|
|
|
631
726
|
getAccounts(...args) {
|
|
632
727
|
return this.sdk.GetAccounts(...args);
|
|
633
728
|
}
|
|
634
|
-
// Individual event queries
|
|
729
|
+
// Individual event queries
|
|
635
730
|
getReleasedEvents(...args) {
|
|
636
731
|
return this.sdk.GetReleasedEvents(...args);
|
|
637
732
|
}
|
|
@@ -649,7 +744,278 @@ var SlotsClient = class {
|
|
|
649
744
|
}
|
|
650
745
|
// Meta
|
|
651
746
|
getMeta() {
|
|
652
|
-
return this.
|
|
747
|
+
return this.gqlClient.request(META_QUERY);
|
|
748
|
+
}
|
|
749
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
750
|
+
// READ — On-chain (RPC)
|
|
751
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
752
|
+
/** Read full slot info from on-chain (RPC, not subgraph). */
|
|
753
|
+
async getSlotInfo(slot) {
|
|
754
|
+
return this.publicClient.readContract({
|
|
755
|
+
address: slot,
|
|
756
|
+
abi: slotAbi,
|
|
757
|
+
functionName: "getSlotInfo"
|
|
758
|
+
});
|
|
759
|
+
}
|
|
760
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
761
|
+
// WRITE — Factory Functions
|
|
762
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
763
|
+
async createSlot(params) {
|
|
764
|
+
return this.wallet.writeContract({
|
|
765
|
+
address: this.factory,
|
|
766
|
+
abi: slotFactoryAbi,
|
|
767
|
+
functionName: "createSlot",
|
|
768
|
+
args: [params.recipient, params.currency, params.config, params.initParams],
|
|
769
|
+
account: this.wallet.account,
|
|
770
|
+
chain: this.wallet.chain
|
|
771
|
+
});
|
|
772
|
+
}
|
|
773
|
+
async createSlots(params) {
|
|
774
|
+
return this.wallet.writeContract({
|
|
775
|
+
address: this.factory,
|
|
776
|
+
abi: slotFactoryAbi,
|
|
777
|
+
functionName: "createSlots",
|
|
778
|
+
args: [params.recipient, params.currency, params.config, params.initParams, params.count],
|
|
779
|
+
account: this.wallet.account,
|
|
780
|
+
chain: this.wallet.chain
|
|
781
|
+
});
|
|
782
|
+
}
|
|
783
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
784
|
+
// WRITE — Slot Functions
|
|
785
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
786
|
+
/** Buy a slot (or force buy an occupied one). Handles ERC-20 approval automatically. */
|
|
787
|
+
async buy(params) {
|
|
788
|
+
return this.withAllowance(params.slot, params.depositAmount, {
|
|
789
|
+
to: params.slot,
|
|
790
|
+
abi: slotAbi,
|
|
791
|
+
functionName: "buy",
|
|
792
|
+
args: [params.depositAmount, params.selfAssessedPrice]
|
|
793
|
+
});
|
|
794
|
+
}
|
|
795
|
+
/** Self-assess a new price for an occupied slot (occupant only). */
|
|
796
|
+
async selfAssess(slot, newPrice) {
|
|
797
|
+
return this.wallet.writeContract({
|
|
798
|
+
address: slot,
|
|
799
|
+
abi: slotAbi,
|
|
800
|
+
functionName: "selfAssess",
|
|
801
|
+
args: [newPrice],
|
|
802
|
+
account: this.wallet.account,
|
|
803
|
+
chain: this.wallet.chain
|
|
804
|
+
});
|
|
805
|
+
}
|
|
806
|
+
/** Top up deposit on a slot (occupant only). Handles ERC-20 approval automatically. */
|
|
807
|
+
async topUp(slot, amount) {
|
|
808
|
+
return this.withAllowance(slot, amount, {
|
|
809
|
+
to: slot,
|
|
810
|
+
abi: slotAbi,
|
|
811
|
+
functionName: "topUp",
|
|
812
|
+
args: [amount]
|
|
813
|
+
});
|
|
814
|
+
}
|
|
815
|
+
/** Withdraw from deposit (occupant only). Cannot go below minimum deposit. */
|
|
816
|
+
async withdraw(slot, amount) {
|
|
817
|
+
return this.wallet.writeContract({
|
|
818
|
+
address: slot,
|
|
819
|
+
abi: slotAbi,
|
|
820
|
+
functionName: "withdraw",
|
|
821
|
+
args: [amount],
|
|
822
|
+
account: this.wallet.account,
|
|
823
|
+
chain: this.wallet.chain
|
|
824
|
+
});
|
|
825
|
+
}
|
|
826
|
+
/** Release a slot (occupant only). Returns remaining deposit. */
|
|
827
|
+
async release(slot) {
|
|
828
|
+
return this.wallet.writeContract({
|
|
829
|
+
address: slot,
|
|
830
|
+
abi: slotAbi,
|
|
831
|
+
functionName: "release",
|
|
832
|
+
account: this.wallet.account,
|
|
833
|
+
chain: this.wallet.chain
|
|
834
|
+
});
|
|
835
|
+
}
|
|
836
|
+
/** Collect accumulated tax (permissionless). */
|
|
837
|
+
async collect(slot) {
|
|
838
|
+
return this.wallet.writeContract({
|
|
839
|
+
address: slot,
|
|
840
|
+
abi: slotAbi,
|
|
841
|
+
functionName: "collect",
|
|
842
|
+
account: this.wallet.account,
|
|
843
|
+
chain: this.wallet.chain
|
|
844
|
+
});
|
|
845
|
+
}
|
|
846
|
+
/** Liquidate an insolvent slot (permissionless). Caller receives bounty. */
|
|
847
|
+
async liquidate(slot) {
|
|
848
|
+
return this.wallet.writeContract({
|
|
849
|
+
address: slot,
|
|
850
|
+
abi: slotAbi,
|
|
851
|
+
functionName: "liquidate",
|
|
852
|
+
account: this.wallet.account,
|
|
853
|
+
chain: this.wallet.chain
|
|
854
|
+
});
|
|
855
|
+
}
|
|
856
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
857
|
+
// WRITE — Manager Functions
|
|
858
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
859
|
+
/** Propose a tax rate update (manager only, slot must have mutableTax). */
|
|
860
|
+
async proposeTaxUpdate(slot, newPct) {
|
|
861
|
+
return this.wallet.writeContract({
|
|
862
|
+
address: slot,
|
|
863
|
+
abi: slotAbi,
|
|
864
|
+
functionName: "proposeTaxUpdate",
|
|
865
|
+
args: [newPct],
|
|
866
|
+
account: this.wallet.account,
|
|
867
|
+
chain: this.wallet.chain
|
|
868
|
+
});
|
|
869
|
+
}
|
|
870
|
+
/** Propose a module update (manager only, slot must have mutableModule). */
|
|
871
|
+
async proposeModuleUpdate(slot, newModule) {
|
|
872
|
+
return this.wallet.writeContract({
|
|
873
|
+
address: slot,
|
|
874
|
+
abi: slotAbi,
|
|
875
|
+
functionName: "proposeModuleUpdate",
|
|
876
|
+
args: [newModule],
|
|
877
|
+
account: this.wallet.account,
|
|
878
|
+
chain: this.wallet.chain
|
|
879
|
+
});
|
|
880
|
+
}
|
|
881
|
+
/** Cancel pending updates (manager only). */
|
|
882
|
+
async cancelPendingUpdates(slot) {
|
|
883
|
+
return this.wallet.writeContract({
|
|
884
|
+
address: slot,
|
|
885
|
+
abi: slotAbi,
|
|
886
|
+
functionName: "cancelPendingUpdates",
|
|
887
|
+
account: this.wallet.account,
|
|
888
|
+
chain: this.wallet.chain
|
|
889
|
+
});
|
|
890
|
+
}
|
|
891
|
+
/** Set liquidation bounty bps (manager only). */
|
|
892
|
+
async setLiquidationBounty(slot, newBps) {
|
|
893
|
+
return this.wallet.writeContract({
|
|
894
|
+
address: slot,
|
|
895
|
+
abi: slotAbi,
|
|
896
|
+
functionName: "setLiquidationBounty",
|
|
897
|
+
args: [newBps],
|
|
898
|
+
account: this.wallet.account,
|
|
899
|
+
chain: this.wallet.chain
|
|
900
|
+
});
|
|
901
|
+
}
|
|
902
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
903
|
+
// WRITE — Multicall
|
|
904
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
905
|
+
/** Batch multiple slot calls into one transaction via multicall. */
|
|
906
|
+
async multicall(slot, calls) {
|
|
907
|
+
const data = calls.map(
|
|
908
|
+
(call) => encodeFunctionData({
|
|
909
|
+
abi: slotAbi,
|
|
910
|
+
functionName: call.functionName,
|
|
911
|
+
args: call.args
|
|
912
|
+
})
|
|
913
|
+
);
|
|
914
|
+
return this.wallet.writeContract({
|
|
915
|
+
address: slot,
|
|
916
|
+
abi: slotAbi,
|
|
917
|
+
functionName: "multicall",
|
|
918
|
+
args: [data],
|
|
919
|
+
account: this.wallet.account,
|
|
920
|
+
chain: this.wallet.chain
|
|
921
|
+
});
|
|
922
|
+
}
|
|
923
|
+
// ─── Internals ──────────────────────────────────────────────────────────────
|
|
924
|
+
/** Check if wallet supports atomic batch calls (EIP-5792). */
|
|
925
|
+
async supportsAtomicBatch() {
|
|
926
|
+
if (this._atomicSupport !== void 0) return this._atomicSupport;
|
|
927
|
+
try {
|
|
928
|
+
const capabilities = await this.wallet.getCapabilities?.();
|
|
929
|
+
if (!capabilities) {
|
|
930
|
+
this._atomicSupport = false;
|
|
931
|
+
return false;
|
|
932
|
+
}
|
|
933
|
+
const chainId = this.wallet.chain.id;
|
|
934
|
+
const chainCaps = capabilities[chainId] || capabilities[`0x${chainId.toString(16)}`];
|
|
935
|
+
const atomic = chainCaps?.atomicBatch ?? chainCaps?.atomic;
|
|
936
|
+
const status = atomic && typeof atomic === "object" && "status" in atomic ? atomic.status : void 0;
|
|
937
|
+
this._atomicSupport = status === "supported" || status === "ready";
|
|
938
|
+
} catch {
|
|
939
|
+
this._atomicSupport = false;
|
|
940
|
+
}
|
|
941
|
+
return this._atomicSupport;
|
|
942
|
+
}
|
|
943
|
+
/** Poll EIP-5792 getCallsStatus until the batch settles, then return a tx hash. */
|
|
944
|
+
async pollBatchReceipt(id) {
|
|
945
|
+
const MAX_ATTEMPTS = 60;
|
|
946
|
+
const INTERVAL = 1500;
|
|
947
|
+
for (let i = 0; i < MAX_ATTEMPTS; i++) {
|
|
948
|
+
const result = await this.wallet.getCallsStatus({ id });
|
|
949
|
+
if (result.status === "success") {
|
|
950
|
+
const receipts = result.receipts ?? [];
|
|
951
|
+
return receipts[receipts.length - 1]?.transactionHash ?? id;
|
|
952
|
+
}
|
|
953
|
+
if (result.status === "failure") {
|
|
954
|
+
throw new Error("Batch transaction reverted");
|
|
955
|
+
}
|
|
956
|
+
await new Promise((r) => setTimeout(r, INTERVAL));
|
|
957
|
+
}
|
|
958
|
+
throw new Error("Batch transaction timed out");
|
|
959
|
+
}
|
|
960
|
+
/**
|
|
961
|
+
* Execute a contract call that needs ERC-20 allowance.
|
|
962
|
+
* If wallet supports atomic batch (EIP-5792): sends approve + action as one atomic call.
|
|
963
|
+
* Otherwise: chains approve tx (if needed) then action tx.
|
|
964
|
+
*/
|
|
965
|
+
async withAllowance(spender, amount, call) {
|
|
966
|
+
const currency = await this.publicClient.readContract({
|
|
967
|
+
address: spender,
|
|
968
|
+
abi: slotAbi,
|
|
969
|
+
functionName: "currency"
|
|
970
|
+
});
|
|
971
|
+
const allowance = await this.publicClient.readContract({
|
|
972
|
+
address: currency,
|
|
973
|
+
abi: erc20Abi,
|
|
974
|
+
functionName: "allowance",
|
|
975
|
+
args: [this.account, spender]
|
|
976
|
+
});
|
|
977
|
+
const needsApproval = allowance < amount;
|
|
978
|
+
if (needsApproval && await this.supportsAtomicBatch()) {
|
|
979
|
+
const approveData = encodeFunctionData({
|
|
980
|
+
abi: erc20Abi,
|
|
981
|
+
functionName: "approve",
|
|
982
|
+
args: [spender, amount]
|
|
983
|
+
});
|
|
984
|
+
const actionData = encodeFunctionData({
|
|
985
|
+
abi: call.abi,
|
|
986
|
+
functionName: call.functionName,
|
|
987
|
+
args: call.args
|
|
988
|
+
});
|
|
989
|
+
const id = await this.wallet.sendCalls({
|
|
990
|
+
account: this.wallet.account,
|
|
991
|
+
chain: this.wallet.chain,
|
|
992
|
+
calls: [
|
|
993
|
+
{ to: currency, data: approveData },
|
|
994
|
+
{ to: call.to, data: actionData }
|
|
995
|
+
]
|
|
996
|
+
});
|
|
997
|
+
const txHash = await this.pollBatchReceipt(id);
|
|
998
|
+
return txHash;
|
|
999
|
+
}
|
|
1000
|
+
if (needsApproval) {
|
|
1001
|
+
const hash = await this.wallet.writeContract({
|
|
1002
|
+
address: currency,
|
|
1003
|
+
abi: erc20Abi,
|
|
1004
|
+
functionName: "approve",
|
|
1005
|
+
args: [spender, amount],
|
|
1006
|
+
account: this.wallet.account,
|
|
1007
|
+
chain: this.wallet.chain
|
|
1008
|
+
});
|
|
1009
|
+
await this.publicClient.waitForTransactionReceipt({ hash });
|
|
1010
|
+
}
|
|
1011
|
+
return this.wallet.writeContract({
|
|
1012
|
+
address: call.to,
|
|
1013
|
+
abi: call.abi,
|
|
1014
|
+
functionName: call.functionName,
|
|
1015
|
+
args: call.args,
|
|
1016
|
+
account: this.wallet.account,
|
|
1017
|
+
chain: this.wallet.chain
|
|
1018
|
+
});
|
|
653
1019
|
}
|
|
654
1020
|
};
|
|
655
1021
|
function createSlotsClient(config) {
|