@satoshai/kit 0.4.0 → 0.5.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 +23 -0
- package/dist/index.cjs +254 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +73 -4
- package/dist/index.d.ts +73 -4
- package/dist/index.js +254 -7
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -10,6 +10,7 @@ Typesafe Stacks wallet & contract interaction library for React. Wagmi-inspired
|
|
|
10
10
|
- **`useAddress`** — Access connected wallet address and status
|
|
11
11
|
- **`useSignMessage`** — Sign arbitrary messages
|
|
12
12
|
- **`useWriteContract`** — Call smart contracts with post-conditions
|
|
13
|
+
- **`useTransferSTX`** — Native STX transfers
|
|
13
14
|
- **`useBnsName`** — Resolve BNS v2 names
|
|
14
15
|
- **6 wallets supported** — Xverse, Leather, OKX, Asigna, Fordefi, WalletConnect
|
|
15
16
|
- **Next.js App Router compatible** — `"use client"` directives included
|
|
@@ -179,6 +180,28 @@ signMessage({ message: 'Hello Stacks' }, {
|
|
|
179
180
|
const { publicKey, signature } = await signMessageAsync({ message: 'Hello Stacks' });
|
|
180
181
|
```
|
|
181
182
|
|
|
183
|
+
### `useTransferSTX()`
|
|
184
|
+
|
|
185
|
+
```ts
|
|
186
|
+
const { transferSTX, transferSTXAsync, data, error, isPending } = useTransferSTX();
|
|
187
|
+
|
|
188
|
+
// Callback style
|
|
189
|
+
transferSTX({
|
|
190
|
+
recipient: 'SP2...',
|
|
191
|
+
amount: 1000000n, // in microSTX
|
|
192
|
+
memo: 'optional memo',
|
|
193
|
+
}, {
|
|
194
|
+
onSuccess: (txid) => {},
|
|
195
|
+
onError: (error) => {},
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// Async style
|
|
199
|
+
const txid = await transferSTXAsync({
|
|
200
|
+
recipient: 'SP2...',
|
|
201
|
+
amount: 1000000n,
|
|
202
|
+
});
|
|
203
|
+
```
|
|
204
|
+
|
|
182
205
|
### `useWriteContract()`
|
|
183
206
|
|
|
184
207
|
```ts
|
package/dist/index.cjs
CHANGED
|
@@ -286,6 +286,7 @@ var StacksWalletProvider = ({
|
|
|
286
286
|
const [provider, setProvider] = react.useState();
|
|
287
287
|
const [isConnecting, setIsConnecting] = react.useState(false);
|
|
288
288
|
const connectGenRef = react.useRef(0);
|
|
289
|
+
const wasConnectedRef = react.useRef(false);
|
|
289
290
|
const wcInitRef = react.useRef(null);
|
|
290
291
|
const walletConnectRef = react.useRef(walletConnect);
|
|
291
292
|
walletConnectRef.current = walletConnect;
|
|
@@ -509,8 +510,11 @@ var StacksWalletProvider = ({
|
|
|
509
510
|
);
|
|
510
511
|
}, [address, provider]);
|
|
511
512
|
react.useEffect(() => {
|
|
512
|
-
|
|
513
|
-
|
|
513
|
+
const isConnected = !!address && !!provider;
|
|
514
|
+
if (isConnected && !wasConnectedRef.current) {
|
|
515
|
+
onConnect?.(provider, address);
|
|
516
|
+
}
|
|
517
|
+
wasConnectedRef.current = isConnected;
|
|
514
518
|
}, [address, provider, onConnect]);
|
|
515
519
|
const handleAddressChange = react.useCallback(
|
|
516
520
|
(newAddress) => {
|
|
@@ -759,10 +763,239 @@ var getNetworkFromAddress = (address) => {
|
|
|
759
763
|
}
|
|
760
764
|
throw new Error(`Invalid Stacks address: ${address}`);
|
|
761
765
|
};
|
|
766
|
+
|
|
767
|
+
// src/hooks/use-transfer-stx.ts
|
|
768
|
+
var useTransferSTX = () => {
|
|
769
|
+
const { isConnected, address, provider } = useAddress();
|
|
770
|
+
const [data, setData] = react.useState(void 0);
|
|
771
|
+
const [error, setError] = react.useState(null);
|
|
772
|
+
const [status, setStatus] = react.useState("idle");
|
|
773
|
+
const transferSTXAsync = react.useCallback(
|
|
774
|
+
async (variables) => {
|
|
775
|
+
if (!isConnected || !address) {
|
|
776
|
+
throw new Error("Wallet is not connected");
|
|
777
|
+
}
|
|
778
|
+
setStatus("pending");
|
|
779
|
+
setError(null);
|
|
780
|
+
setData(void 0);
|
|
781
|
+
try {
|
|
782
|
+
if (provider === "okx") {
|
|
783
|
+
if (!window.okxwallet) {
|
|
784
|
+
throw new Error("OKX wallet not found");
|
|
785
|
+
}
|
|
786
|
+
const response2 = await window.okxwallet.stacks.signTransaction({
|
|
787
|
+
txType: "token_transfer",
|
|
788
|
+
recipient: variables.recipient,
|
|
789
|
+
amount: String(variables.amount),
|
|
790
|
+
memo: variables.memo ?? "",
|
|
791
|
+
stxAddress: address,
|
|
792
|
+
anchorMode: 3
|
|
793
|
+
});
|
|
794
|
+
setData(response2.txHash);
|
|
795
|
+
setStatus("success");
|
|
796
|
+
return response2.txHash;
|
|
797
|
+
}
|
|
798
|
+
const response = await connect.request("stx_transferStx", {
|
|
799
|
+
recipient: variables.recipient,
|
|
800
|
+
amount: variables.amount,
|
|
801
|
+
...variables.memo !== void 0 && {
|
|
802
|
+
memo: variables.memo
|
|
803
|
+
},
|
|
804
|
+
...variables.fee !== void 0 && {
|
|
805
|
+
fee: variables.fee
|
|
806
|
+
},
|
|
807
|
+
...variables.nonce !== void 0 && {
|
|
808
|
+
nonce: variables.nonce
|
|
809
|
+
},
|
|
810
|
+
network: getNetworkFromAddress(address)
|
|
811
|
+
});
|
|
812
|
+
if (!response.txid) {
|
|
813
|
+
throw new Error("No transaction ID returned");
|
|
814
|
+
}
|
|
815
|
+
setData(response.txid);
|
|
816
|
+
setStatus("success");
|
|
817
|
+
return response.txid;
|
|
818
|
+
} catch (err) {
|
|
819
|
+
const error2 = err instanceof Error ? err : new Error(String(err));
|
|
820
|
+
setError(error2);
|
|
821
|
+
setStatus("error");
|
|
822
|
+
throw error2;
|
|
823
|
+
}
|
|
824
|
+
},
|
|
825
|
+
[isConnected, address, provider]
|
|
826
|
+
);
|
|
827
|
+
const transferSTX = react.useCallback(
|
|
828
|
+
(variables, options) => {
|
|
829
|
+
transferSTXAsync(variables).then((txid) => {
|
|
830
|
+
options?.onSuccess?.(txid);
|
|
831
|
+
options?.onSettled?.(txid, null);
|
|
832
|
+
}).catch((error2) => {
|
|
833
|
+
options?.onError?.(error2);
|
|
834
|
+
options?.onSettled?.(void 0, error2);
|
|
835
|
+
});
|
|
836
|
+
},
|
|
837
|
+
[transferSTXAsync]
|
|
838
|
+
);
|
|
839
|
+
const reset = react.useCallback(() => {
|
|
840
|
+
setData(void 0);
|
|
841
|
+
setError(null);
|
|
842
|
+
setStatus("idle");
|
|
843
|
+
}, []);
|
|
844
|
+
return react.useMemo(
|
|
845
|
+
() => ({
|
|
846
|
+
transferSTX,
|
|
847
|
+
transferSTXAsync,
|
|
848
|
+
reset,
|
|
849
|
+
data,
|
|
850
|
+
error,
|
|
851
|
+
isError: status === "error",
|
|
852
|
+
isIdle: status === "idle",
|
|
853
|
+
isPending: status === "pending",
|
|
854
|
+
isSuccess: status === "success",
|
|
855
|
+
status
|
|
856
|
+
}),
|
|
857
|
+
[transferSTX, transferSTXAsync, reset, data, error, status]
|
|
858
|
+
);
|
|
859
|
+
};
|
|
860
|
+
function toClarityValue(value, abiType) {
|
|
861
|
+
if (typeof abiType === "string") {
|
|
862
|
+
switch (abiType) {
|
|
863
|
+
case "uint128": {
|
|
864
|
+
if (typeof value !== "bigint") {
|
|
865
|
+
throw new Error(
|
|
866
|
+
`@satoshai/kit: Expected bigint (uint128), got ${typeof value}`
|
|
867
|
+
);
|
|
868
|
+
}
|
|
869
|
+
return transactions.uintCV(value);
|
|
870
|
+
}
|
|
871
|
+
case "int128": {
|
|
872
|
+
if (typeof value !== "bigint") {
|
|
873
|
+
throw new Error(
|
|
874
|
+
`@satoshai/kit: Expected bigint (int128), got ${typeof value}`
|
|
875
|
+
);
|
|
876
|
+
}
|
|
877
|
+
return transactions.intCV(value);
|
|
878
|
+
}
|
|
879
|
+
case "bool": {
|
|
880
|
+
if (typeof value !== "boolean") {
|
|
881
|
+
throw new Error(
|
|
882
|
+
`@satoshai/kit: Expected boolean (bool), got ${typeof value}`
|
|
883
|
+
);
|
|
884
|
+
}
|
|
885
|
+
return transactions.boolCV(value);
|
|
886
|
+
}
|
|
887
|
+
case "principal":
|
|
888
|
+
case "trait_reference": {
|
|
889
|
+
if (typeof value !== "string") {
|
|
890
|
+
throw new Error(
|
|
891
|
+
`@satoshai/kit: Expected string (${abiType}), got ${typeof value}`
|
|
892
|
+
);
|
|
893
|
+
}
|
|
894
|
+
if (value.includes(".")) {
|
|
895
|
+
const [addr, ...rest] = value.split(".");
|
|
896
|
+
return transactions.contractPrincipalCV(addr, rest.join("."));
|
|
897
|
+
}
|
|
898
|
+
return transactions.standardPrincipalCV(value);
|
|
899
|
+
}
|
|
900
|
+
case "none":
|
|
901
|
+
return transactions.noneCV();
|
|
902
|
+
default:
|
|
903
|
+
throw new Error(
|
|
904
|
+
`@satoshai/kit: Unsupported ABI type "${abiType}"`
|
|
905
|
+
);
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
if ("buffer" in abiType) {
|
|
909
|
+
if (!(value instanceof Uint8Array)) {
|
|
910
|
+
throw new Error(
|
|
911
|
+
`@satoshai/kit: Expected Uint8Array (buffer), got ${typeof value}`
|
|
912
|
+
);
|
|
913
|
+
}
|
|
914
|
+
return transactions.bufferCV(value);
|
|
915
|
+
}
|
|
916
|
+
if ("string-ascii" in abiType) {
|
|
917
|
+
if (typeof value !== "string") {
|
|
918
|
+
throw new Error(
|
|
919
|
+
`@satoshai/kit: Expected string (string-ascii), got ${typeof value}`
|
|
920
|
+
);
|
|
921
|
+
}
|
|
922
|
+
return transactions.stringAsciiCV(value);
|
|
923
|
+
}
|
|
924
|
+
if ("string-utf8" in abiType) {
|
|
925
|
+
if (typeof value !== "string") {
|
|
926
|
+
throw new Error(
|
|
927
|
+
`@satoshai/kit: Expected string (string-utf8), got ${typeof value}`
|
|
928
|
+
);
|
|
929
|
+
}
|
|
930
|
+
return transactions.stringUtf8CV(value);
|
|
931
|
+
}
|
|
932
|
+
if ("optional" in abiType) {
|
|
933
|
+
if (value === null || value === void 0) return transactions.noneCV();
|
|
934
|
+
return transactions.someCV(toClarityValue(value, abiType.optional));
|
|
935
|
+
}
|
|
936
|
+
if ("list" in abiType) {
|
|
937
|
+
if (!Array.isArray(value)) {
|
|
938
|
+
throw new Error(
|
|
939
|
+
`@satoshai/kit: Expected array (list), got ${typeof value}`
|
|
940
|
+
);
|
|
941
|
+
}
|
|
942
|
+
const listType = abiType.list;
|
|
943
|
+
return transactions.listCV(
|
|
944
|
+
value.map((item) => toClarityValue(item, listType.type))
|
|
945
|
+
);
|
|
946
|
+
}
|
|
947
|
+
if ("tuple" in abiType) {
|
|
948
|
+
if (typeof value !== "object" || value === null) {
|
|
949
|
+
throw new Error(
|
|
950
|
+
`@satoshai/kit: Expected object (tuple), got ${value === null ? "null" : typeof value}`
|
|
951
|
+
);
|
|
952
|
+
}
|
|
953
|
+
const entries = abiType.tuple;
|
|
954
|
+
const obj = value;
|
|
955
|
+
const result = {};
|
|
956
|
+
for (const entry of entries) {
|
|
957
|
+
result[entry.name] = toClarityValue(obj[entry.name], entry.type);
|
|
958
|
+
}
|
|
959
|
+
return transactions.tupleCV(result);
|
|
960
|
+
}
|
|
961
|
+
throw new Error(
|
|
962
|
+
`@satoshai/kit: Unsupported ABI type: ${JSON.stringify(abiType)}`
|
|
963
|
+
);
|
|
964
|
+
}
|
|
965
|
+
function namedArgsToClarityValues(args, abiArgs) {
|
|
966
|
+
return abiArgs.map((abiArg) => {
|
|
967
|
+
if (!(abiArg.name in args)) {
|
|
968
|
+
throw new Error(
|
|
969
|
+
`@satoshai/kit: Missing argument "${abiArg.name}"`
|
|
970
|
+
);
|
|
971
|
+
}
|
|
972
|
+
return toClarityValue(
|
|
973
|
+
args[abiArg.name],
|
|
974
|
+
abiArg.type
|
|
975
|
+
);
|
|
976
|
+
});
|
|
977
|
+
}
|
|
762
978
|
var preparePostConditionsForOKX = (postConditions) => postConditions.map((pc) => transactions.postConditionToHex(pc));
|
|
763
979
|
var prepareArgsForOKX = (args) => args.map((arg) => transactions.cvToHex(arg));
|
|
764
980
|
|
|
765
981
|
// src/hooks/use-write-contract/use-write-contract.ts
|
|
982
|
+
function resolveArgs(variables) {
|
|
983
|
+
if (!variables.abi) {
|
|
984
|
+
return variables.args;
|
|
985
|
+
}
|
|
986
|
+
const fn = variables.abi.functions.find(
|
|
987
|
+
(f) => f.name === variables.functionName && f.access === "public"
|
|
988
|
+
);
|
|
989
|
+
if (!fn) {
|
|
990
|
+
throw new Error(
|
|
991
|
+
`@satoshai/kit: Public function "${variables.functionName}" not found in ABI`
|
|
992
|
+
);
|
|
993
|
+
}
|
|
994
|
+
return namedArgsToClarityValues(
|
|
995
|
+
variables.args,
|
|
996
|
+
fn.args
|
|
997
|
+
);
|
|
998
|
+
}
|
|
766
999
|
var useWriteContract = () => {
|
|
767
1000
|
const { isConnected, address, provider } = useAddress();
|
|
768
1001
|
const [data, setData] = react.useState(void 0);
|
|
@@ -776,6 +1009,7 @@ var useWriteContract = () => {
|
|
|
776
1009
|
setStatus("pending");
|
|
777
1010
|
setError(null);
|
|
778
1011
|
setData(void 0);
|
|
1012
|
+
const resolvedArgs = resolveArgs(variables);
|
|
779
1013
|
try {
|
|
780
1014
|
if (provider === "okx") {
|
|
781
1015
|
if (!window.okxwallet) {
|
|
@@ -785,7 +1019,7 @@ var useWriteContract = () => {
|
|
|
785
1019
|
contractAddress: variables.address,
|
|
786
1020
|
contractName: variables.contract,
|
|
787
1021
|
functionName: variables.functionName,
|
|
788
|
-
functionArgs: prepareArgsForOKX(
|
|
1022
|
+
functionArgs: prepareArgsForOKX(resolvedArgs),
|
|
789
1023
|
postConditions: preparePostConditionsForOKX(
|
|
790
1024
|
variables.pc.postConditions
|
|
791
1025
|
),
|
|
@@ -802,7 +1036,7 @@ var useWriteContract = () => {
|
|
|
802
1036
|
address,
|
|
803
1037
|
contract: `${variables.address}.${variables.contract}`,
|
|
804
1038
|
functionName: variables.functionName,
|
|
805
|
-
functionArgs:
|
|
1039
|
+
functionArgs: resolvedArgs,
|
|
806
1040
|
postConditions: variables.pc.postConditions,
|
|
807
1041
|
postConditionMode: variables.pc.mode === transactions.PostConditionMode.Allow ? "allow" : "deny",
|
|
808
1042
|
network: getNetworkFromAddress(address)
|
|
@@ -864,20 +1098,28 @@ var useBnsName = (address) => {
|
|
|
864
1098
|
setIsLoading(false);
|
|
865
1099
|
return;
|
|
866
1100
|
}
|
|
1101
|
+
let cancelled = false;
|
|
867
1102
|
const fetchBnsName = async () => {
|
|
868
1103
|
setIsLoading(true);
|
|
869
1104
|
try {
|
|
870
1105
|
const network = getNetworkFromAddress(address);
|
|
871
1106
|
const result = await bnsV2Sdk.getPrimaryName({ address, network });
|
|
1107
|
+
if (cancelled) return;
|
|
872
1108
|
const fullName = result ? `${result.name}.${result.namespace}` : null;
|
|
873
1109
|
setBnsName(fullName);
|
|
874
1110
|
} catch {
|
|
1111
|
+
if (cancelled) return;
|
|
875
1112
|
setBnsName(null);
|
|
876
1113
|
} finally {
|
|
877
|
-
|
|
1114
|
+
if (!cancelled) {
|
|
1115
|
+
setIsLoading(false);
|
|
1116
|
+
}
|
|
878
1117
|
}
|
|
879
1118
|
};
|
|
880
1119
|
void fetchBnsName();
|
|
1120
|
+
return () => {
|
|
1121
|
+
cancelled = true;
|
|
1122
|
+
};
|
|
881
1123
|
}, [address]);
|
|
882
1124
|
return { bnsName, isLoading };
|
|
883
1125
|
};
|
|
@@ -886,8 +1128,14 @@ var useWallets = () => {
|
|
|
886
1128
|
return react.useMemo(() => ({ wallets }), [wallets]);
|
|
887
1129
|
};
|
|
888
1130
|
|
|
1131
|
+
// src/utils/create-contract-config.ts
|
|
1132
|
+
function createContractConfig(config) {
|
|
1133
|
+
return config;
|
|
1134
|
+
}
|
|
1135
|
+
|
|
889
1136
|
exports.SUPPORTED_STACKS_WALLETS = SUPPORTED_STACKS_WALLETS;
|
|
890
1137
|
exports.StacksWalletProvider = StacksWalletProvider;
|
|
1138
|
+
exports.createContractConfig = createContractConfig;
|
|
891
1139
|
exports.getLocalStorageWallet = getLocalStorageWallet;
|
|
892
1140
|
exports.getNetworkFromAddress = getNetworkFromAddress;
|
|
893
1141
|
exports.getStacksWallets = getStacksWallets;
|
|
@@ -896,6 +1144,7 @@ exports.useBnsName = useBnsName;
|
|
|
896
1144
|
exports.useConnect = useConnect;
|
|
897
1145
|
exports.useDisconnect = useDisconnect;
|
|
898
1146
|
exports.useSignMessage = useSignMessage;
|
|
1147
|
+
exports.useTransferSTX = useTransferSTX;
|
|
899
1148
|
exports.useWallets = useWallets;
|
|
900
1149
|
exports.useWriteContract = useWriteContract;
|
|
901
1150
|
//# sourceMappingURL=index.cjs.map
|