Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 105 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,18 @@ await zetachainCall(
All Toolkit capabilities are also exposed through [`zetachain`
CLI](https://github.com/zeta-chain/cli).

## Variables

### suiBrowserOptionsSchema

> `const` **suiBrowserOptionsSchema**: `ZodObject`\<\{ `chainId`: `ZodEnum`\<\[`"105"`, `"103"`, `"104"`\]\>; `gasLimit`: `ZodOptional`\<`ZodString`\>; `gatewayObject`: `ZodOptional`\<`ZodString`\>; `gatewayPackage`: `ZodOptional`\<`ZodString`\>; `signerAddress`: `ZodOptional`\<`ZodString`\>; \}, `"strip"`, `ZodTypeAny`, \{ `chainId`: `"105"` \| `"103"` \| `"104"`; `gasLimit?`: `string`; `gatewayObject?`: `string`; `gatewayPackage?`: `string`; `signerAddress?`: `string`; \}, \{ `chainId`: `"105"` \| `"103"` \| `"104"`; `gasLimit?`: `string`; `gatewayObject?`: `string`; `gatewayPackage?`: `string`; `signerAddress?`: `string`; \}\>

***

### suiDepositAndCallParamsSchema

> `const` **suiDepositAndCallParamsSchema**: `ZodObject`\<\{ `amount`: `ZodString`; `receiver`: `ZodString`; `token`: `ZodOptional`\<`ZodString`\>; `types`: `ZodArray`\<`ZodString`, `"many"`\>; `values`: `ZodArray`\<`ZodUnion`\<\[`ZodString`, `ZodBigInt`, `ZodBoolean`\]\>, `"many"`\>; \}, `"strip"`, `ZodTypeAny`, \{ `amount`: `string`; `receiver`: `string`; `token?`: `string`; `types`: `string`[]; `values`: (`string` \| `bigint` \| `boolean`)[]; \}, \{ `amount`: `string`; `receiver`: `string`; `token?`: `string`; `types`: `string`[]; `values`: (`string` \| `bigint` \| `boolean`)[]; \}\>

## Functions

### evmCall()
Expand Down Expand Up @@ -331,6 +343,99 @@ Promise that resolves to the transaction receipt

***

### prepareSuiDepositAndCall()

> **prepareSuiDepositAndCall**(`params`, `options`): `Promise`\<\{ `client`: `SuiJsonRpcClient`; `transaction`: `Transaction`; \}\>

Prepares a Sui deposit and call transaction for browser wallet signing.

This function is designed for browser environments where wallet adapters
(like Slush, Sui Wallet, etc.) handle transaction signing. It builds the
same transaction as suiDepositAndCall but returns the unsigned Transaction
object instead of executing it.

#### Parameters

##### params

The deposit and call parameters including amount, receiver, coin type, function types/values

###### amount

`string` = `...`

###### receiver

`string` = `...`

###### token?

`string` = `...`

###### types

`string`[] = `...`

###### values

(`string` \| `bigint` \| `boolean`)[] = `...`

##### options

Configuration options including chain ID, gas limit, gateway settings, and optional signer address

###### chainId

`"105"` \| `"103"` \| `"104"` = `...`

###### gasLimit?

`string` = `...`

###### gatewayObject?

`string` = `...`

###### gatewayPackage?

`string` = `...`

###### signerAddress?

`string` = `...`

#### Returns

`Promise`\<\{ `client`: `SuiJsonRpcClient`; `transaction`: `Transaction`; \}\>

Object containing the unsigned Transaction and SuiClient

#### Example

```typescript
// In a browser with wallet adapter
const { transaction, client } = await prepareSuiDepositAndCall(
{
amount: "0.1",
receiver: "0x123...",
types: ["string"],
values: ["hello"]
},
{
chainId: "105",
signerAddress: "0xabc..." // Current wallet address
}
);

// Sign and execute with wallet adapter
const result = await walletAdapter.signAndExecuteTransaction({
transaction,
options: { showEffects: true }
});
```

***

### solanaCall()

> **solanaCall**(`params`, `options`): `Promise`\<`string`\>
Expand Down
7 changes: 7 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@
"import": "./dist/esm/src/chains/solana/index.js",
"default": "./dist/cjs/src/chains/solana/index.js"
},
"./chains/sui": {
"types": "./dist/types/src/chains/sui/index.d.ts",
"import": "./dist/esm/src/chains/sui/index.js",
"default": "./dist/cjs/src/chains/sui/index.js"
},
"./tasks": {
"types": "./dist/types/packages/tasks/src/index.d.ts",
"import": "./dist/esm/packages/tasks/src/index.js",
Expand Down Expand Up @@ -156,6 +161,8 @@
"@nomiclabs/hardhat-ethers": "^2.2.3",
"@openzeppelin/contracts": "^5.0.2",
"@openzeppelin/contracts-upgradeable": "^5.0.2",
"@scure/bip32": "^2.0.1",
"@scure/bip39": "^2.0.1",
"@solana/wallet-adapter-react": "^0.15.35",
"@solana/web3.js": "1.95.8",
"@ton/core": "^0.60.1",
Expand Down
146 changes: 125 additions & 21 deletions src/chains/sui/depositAndCall.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { AbiCoder, ethers } from "ethers";
import { z } from "zod";

import {
chainIds,
GAS_BUDGET,
getCoin,
getSuiGatewayAndClient,
Expand All @@ -11,35 +12,37 @@ import {
} from "../../../utils/sui";
import { validateAndParseSchema } from "../../../utils/validateAndParseSchema";
import {
suiBrowserOptionsSchema,
suiDepositAndCallParamsSchema,
suiOptionsSchema,
} from "../../schemas/sui";

type suiDepositAndCallParams = z.infer<typeof suiDepositAndCallParamsSchema>;
type suiOptions = z.infer<typeof suiOptionsSchema>;
type suiBrowserOptions = z.infer<typeof suiBrowserOptionsSchema>;

/**
* Deposits tokens and makes a cross-chain call from Sui to a universal contract on ZetaChain.
* Builds a Sui deposit and call transaction (shared logic for CLI and browser).
*
* This function combines token deposit with a contract call in a single transaction.
* It allows you to transfer tokens from Sui to ZetaChain and immediately
* execute a function call on the universal contract. Supports both native SUI
* and other coin types.
*
* @param params - The deposit and call parameters including amount, receiver, coin type, function types/values
* @param options - Configuration options including chain ID, gas limit, gateway settings, and signer
* @returns Promise that resolves when the transaction is executed
* @internal
* @param validatedParams - Validated deposit and call parameters
* @param validatedOptions - Validated options (either CLI or browser)
* @param signerAddress - Optional signer address (required for non-native tokens)
* @returns Object containing the SuiClient and unsigned Transaction
*/
export const suiDepositAndCall = async (
params: suiDepositAndCallParams,
options: suiOptions
) => {
const validatedParams = validateAndParseSchema(
params,
suiDepositAndCallParamsSchema
);
const validatedOptions = validateAndParseSchema(options, suiOptionsSchema);

const buildSuiDepositAndCallTransaction = async (
validatedParams: z.infer<typeof suiDepositAndCallParamsSchema>,
validatedOptions: {
chainId: (typeof chainIds)[number];
gasLimit?: string;
gatewayObject?: string;
gatewayPackage?: string;
},
signerAddress?: string
): Promise<{
client: ReturnType<typeof getSuiGatewayAndClient>["client"];
transaction: Transaction;
}> => {
const { gatewayPackage, gatewayObject, client } = getSuiGatewayAndClient(
validatedOptions.chainId,
validatedOptions.gatewayPackage,
Expand All @@ -61,6 +64,7 @@ export const suiDepositAndCall = async (
const payload = tx.pure.vector("u8", payloadBytes);

if (!validatedParams.token || validatedParams.token === "0x2::sui::SUI") {
// Native SUI transfer - no need for signerAddress
const [splitCoin] = tx.splitCoins(tx.gas, [
toSmallestUnit(validatedParams.amount),
]);
Expand All @@ -71,9 +75,16 @@ export const suiDepositAndCall = async (
typeArguments: ["0x2::sui::SUI"],
});
} else {
// Non-native token transfer - requires signerAddress
if (!signerAddress) {
throw new Error(
"signerAddress is required when transferring non-native tokens"
);
}

const coinObjectId = await getCoin(
client,
validatedOptions.signer.toSuiAddress(),
signerAddress,
validatedParams.token
);

Expand All @@ -90,10 +101,103 @@ export const suiDepositAndCall = async (

tx.setGasBudget(gasBudget);

return { client, transaction: tx };
};

/**
* Deposits tokens and makes a cross-chain call from Sui to a universal contract on ZetaChain.
*
* This function combines token deposit with a contract call in a single transaction.
* It allows you to transfer tokens from Sui to ZetaChain and immediately
* execute a function call on the universal contract. Supports both native SUI
* and other coin types.
*
* @param params - The deposit and call parameters including amount, receiver, coin type, function types/values
* @param options - Configuration options including chain ID, gas limit, gateway settings, and signer
* @returns Promise that resolves when the transaction is executed
*/
export const suiDepositAndCall = async (
params: suiDepositAndCallParams,
options: suiOptions
) => {
const validatedParams = validateAndParseSchema(
params,
suiDepositAndCallParamsSchema
);
const validatedOptions = validateAndParseSchema(options, suiOptionsSchema);

const { client, transaction } = await buildSuiDepositAndCallTransaction(
validatedParams,
validatedOptions,
validatedOptions.signer.toSuiAddress()
);

const gasBudget = BigInt(validatedOptions.gasLimit || GAS_BUDGET);

await signAndExecuteTransaction({
client,
gasBudget,
keypair: validatedOptions.signer,
tx,
tx: transaction,
});
};

/**
* Prepares a Sui deposit and call transaction for browser wallet signing.
*
* This function is designed for browser environments where wallet adapters
* (like Slush, Sui Wallet, etc.) handle transaction signing. It builds the
* same transaction as suiDepositAndCall but returns the unsigned Transaction
* object instead of executing it.
*
* @param params - The deposit and call parameters including amount, receiver, coin type, function types/values
* @param options - Configuration options including chain ID, gas limit, gateway settings, and optional signer address
* @returns Object containing the unsigned Transaction and SuiClient
*
* @example
* ```typescript
* // In a browser with wallet adapter
* const { transaction, client } = await prepareSuiDepositAndCall(
* {
* amount: "0.1",
* receiver: "0x123...",
* types: ["string"],
* values: ["hello"]
* },
* {
* chainId: "105",
* signerAddress: "0xabc..." // Current wallet address
* }
* );
*
* // Sign and execute with wallet adapter
* const result = await walletAdapter.signAndExecuteTransaction({
* transaction,
* options: { showEffects: true }
* });
* ```
*/
export const prepareSuiDepositAndCall = async (
params: suiDepositAndCallParams,
options: suiBrowserOptions
): Promise<{
client: ReturnType<typeof getSuiGatewayAndClient>["client"];
transaction: Transaction;
}> => {
const validatedParams = validateAndParseSchema(
params,
suiDepositAndCallParamsSchema
);
const validatedOptions = validateAndParseSchema(
options,
suiBrowserOptionsSchema
);

const { client, transaction } = await buildSuiDepositAndCallTransaction(
validatedParams,
validatedOptions,
validatedOptions.signerAddress
);

return { client, transaction };
};
4 changes: 4 additions & 0 deletions src/chains/sui/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
export {
suiBrowserOptionsSchema,
suiDepositAndCallParamsSchema,
} from "../../schemas/sui";
export * from "./deposit";
export * from "./depositAndCall";
9 changes: 9 additions & 0 deletions src/schemas/sui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@ export const suiOptionsSchema = z.object({
signer: z.instanceof(Ed25519Keypair),
});

// Browser-friendly schema without Ed25519Keypair requirement
export const suiBrowserOptionsSchema = z.object({
chainId: z.enum(chainIds),
gasLimit: z.string().optional(),
gatewayObject: z.string().optional(),
gatewayPackage: z.string().optional(),
signerAddress: z.string().optional(), // Optional: only needed for non-native token transfers
});

export const suiDepositParamsSchema = z.object({
amount: z.string(),
receiver: z.string(),
Expand Down
4 changes: 2 additions & 2 deletions test/addressResolver.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,18 @@ jest.mock("../utils/keyPaths", () => ({
getAccountKeyPath: jest.fn().mockReturnValue("/mock/path/account.json"),
}));

jest.mock("../utils/accounts", () => ({
jest.mock("../utils/getAccountData", () => ({
accountExists: jest.fn(),
getAccountData: jest.fn(),
}));

import { accountExists, getAccountData } from "../utils/accounts";
import {
resolveBitcoinAddress,
resolveEvmAddress,
resolveSolanaAddress,
} from "../utils/addressResolver";
import { safeExists, safeReadFile } from "../utils/fsUtils";
import { accountExists, getAccountData } from "../utils/getAccountData";

// Sample addresses for testing
const VALID_EVM_ADDRESS = "0x71C7656EC7ab88b098defB751B7401B5f6d8976F";
Expand Down
Loading
Loading