@satoshai/kit 0.4.1 → 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 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
@@ -763,10 +763,239 @@ var getNetworkFromAddress = (address) => {
763
763
  }
764
764
  throw new Error(`Invalid Stacks address: ${address}`);
765
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
+ }
766
978
  var preparePostConditionsForOKX = (postConditions) => postConditions.map((pc) => transactions.postConditionToHex(pc));
767
979
  var prepareArgsForOKX = (args) => args.map((arg) => transactions.cvToHex(arg));
768
980
 
769
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
+ }
770
999
  var useWriteContract = () => {
771
1000
  const { isConnected, address, provider } = useAddress();
772
1001
  const [data, setData] = react.useState(void 0);
@@ -780,6 +1009,7 @@ var useWriteContract = () => {
780
1009
  setStatus("pending");
781
1010
  setError(null);
782
1011
  setData(void 0);
1012
+ const resolvedArgs = resolveArgs(variables);
783
1013
  try {
784
1014
  if (provider === "okx") {
785
1015
  if (!window.okxwallet) {
@@ -789,7 +1019,7 @@ var useWriteContract = () => {
789
1019
  contractAddress: variables.address,
790
1020
  contractName: variables.contract,
791
1021
  functionName: variables.functionName,
792
- functionArgs: prepareArgsForOKX(variables.args),
1022
+ functionArgs: prepareArgsForOKX(resolvedArgs),
793
1023
  postConditions: preparePostConditionsForOKX(
794
1024
  variables.pc.postConditions
795
1025
  ),
@@ -806,7 +1036,7 @@ var useWriteContract = () => {
806
1036
  address,
807
1037
  contract: `${variables.address}.${variables.contract}`,
808
1038
  functionName: variables.functionName,
809
- functionArgs: variables.args,
1039
+ functionArgs: resolvedArgs,
810
1040
  postConditions: variables.pc.postConditions,
811
1041
  postConditionMode: variables.pc.mode === transactions.PostConditionMode.Allow ? "allow" : "deny",
812
1042
  network: getNetworkFromAddress(address)
@@ -898,8 +1128,14 @@ var useWallets = () => {
898
1128
  return react.useMemo(() => ({ wallets }), [wallets]);
899
1129
  };
900
1130
 
1131
+ // src/utils/create-contract-config.ts
1132
+ function createContractConfig(config) {
1133
+ return config;
1134
+ }
1135
+
901
1136
  exports.SUPPORTED_STACKS_WALLETS = SUPPORTED_STACKS_WALLETS;
902
1137
  exports.StacksWalletProvider = StacksWalletProvider;
1138
+ exports.createContractConfig = createContractConfig;
903
1139
  exports.getLocalStorageWallet = getLocalStorageWallet;
904
1140
  exports.getNetworkFromAddress = getNetworkFromAddress;
905
1141
  exports.getStacksWallets = getStacksWallets;
@@ -908,6 +1144,7 @@ exports.useBnsName = useBnsName;
908
1144
  exports.useConnect = useConnect;
909
1145
  exports.useDisconnect = useDisconnect;
910
1146
  exports.useSignMessage = useSignMessage;
1147
+ exports.useTransferSTX = useTransferSTX;
911
1148
  exports.useWallets = useWallets;
912
1149
  exports.useWriteContract = useWriteContract;
913
1150
  //# sourceMappingURL=index.cjs.map