@inco/shield-js
v0.1.0
Published
TypeScript SDK for interacting with IncoShield contracts from browsers and Node.js.
Keywords
Readme
IncoShield SDK
TypeScript SDK for interacting with IncoShield contracts from browsers and Node.js.
Installation
Note: This package is not yet published to npm. For now, use it within this monorepo workspace.
Shield Client
The SDK wraps the shield covalidator's gRPC API with a typed TypeScript client. All protobuf details are hidden — you work with hex strings and bigints.
Connect to the covalidator
The client accepts a connect-es transport, so it works in any environment:
import { createShieldClient } from "@inco/shield-js";
// Browser
import { createConnectTransport } from "@connectrpc/connect-web";
const transport = createConnectTransport({ baseUrl: "http://localhost:50051" });
// Node.js
import { createConnectTransport } from "@connectrpc/connect-node";
const transport = createConnectTransport({ baseUrl: "http://localhost:50051", httpVersion: "2" });
const shield = createShieldClient(transport);Issue a transfer
Submit shielded transfers and receive a spending note to execute on-chain:
const note = await shield.issue({
transfer: {
sender: "0x2222222222222222222222222222222222222222",
receiver: "0x3333333333333333333333333333333333333333",
assetId: "0x0000000000000000000000000000000000000000000000000000000000000001",
amount: 1000n,
},
});
// note.spends contains the signed spends to submit on-chain
// note.signer is the covalidator addressIssue a withdrawal
Submit a private-to-public withdrawal intent:
const note = await shield.issueWithdraw({
sender: "0x2222222222222222222222222222222222222222",
to: "0x4444444444444444444444444444444444444444",
assetId: "0x0000000000000000000000000000000000000000000000000000000000000001",
amount: 500n,
assetMovement: {
assetAdapter: erc20AdapterAddress,
assetIdentificationArgs: "0x...",
movementArgs: "0x...",
},
sessionKeyHash: "0x0000000000000000000000000000000000000000000000000000000000000001",
memo: "0x",
}, "0x2222222222222222222222222222222222222222");
// note.spends[0].spendType will be "withdraw"Query a balance
const balance = await shield.getBalance({
address: "0x2222222222222222222222222222222222222222",
assetId: "0x0000000000000000000000000000000000000000000000000000000000000001",
});
console.log(balance.confirmed, balance.pending, balance.available);Contract Interactions
The contracts module provides typed helpers for all IncoShield on-chain operations.
import { contracts } from "@inco/shield-js";Deposit
// 1. Request the TEE attestation. The enclave returns toCiphertext (the
// AEAD-sealed recipient address) and the TEE signature over the deposit.
const attestation = await shield.requestDepositAttestation({
recipient: account.address,
assetAdapter: erc20AdapterAddress,
assetIdentificationArgs: encodedTokenAddress,
memo: "0x",
escapeHatch: "0x",
});
// 2. Submit on-chain with the TEE-issued ciphertext + signature.
const { txHash } = await contracts.deposit(
walletClient,
publicClient,
shieldAddress,
{
toCiphertext: attestation.toCiphertext,
from: account.address,
assetMovement: {
assetAdapter: erc20AdapterAddress,
assetIdentificationArgs: encodedTokenAddress,
movementArgs: encodedAmount,
},
amount: 1000n,
memo: "0x",
escapeHatch: "0x",
},
attestation.teeSignature,
);Transfer, Withdraw, DeFi Interaction
// Transfer (with TEE signature)
await contracts.transfer(walletClient, publicClient, shieldAddress, transferData, signature);
// Withdraw (with TEE signature)
await contracts.withdraw(walletClient, publicClient, shieldAddress, withdrawData, signature);
// DeFi interaction (with TEE signature)
await contracts.performDefiInteraction(walletClient, publicClient, shieldAddress, interaction, signature);Proxy Account Operations
// Execute operations via protocol-managed proxy account
await contracts.performProxyAccountOperation(walletClient, publicClient, shieldAddress, {
account: proxyAddress, // or address(0) to deploy new
nonce: await contracts.getProxyAccountOperationNonce(publicClient, shieldAddress, proxyAddress),
batch: [{ target: tokenAddress, value: 0n, data: calldata }],
escapeHatchData: "0x",
}, signature);
// Query proxy accounts
const proxies = await contracts.getDeployedProxyAccounts(publicClient, shieldAddress);
const isOwned = await contracts.isProxyAccountOwnedByProtocol(publicClient, shieldAddress, proxyAddress);Digests and State
// Get current handle
const handle = await contracts.getHandle(publicClient, shieldAddress);
// Compute operation digests (for TEE signing)
const digest = await contracts.getTransferDigest(publicClient, shieldAddress, transferData);
const digest = await contracts.getWithdrawDigest(publicClient, shieldAddress, withdrawData);
const digest = await contracts.getProxyAccountOperationDigest(publicClient, shieldAddress, operation);Watch Settlement Events
const unwatch = contracts.watchSettlementEvents(publicClient, shieldAddress, {
onTransferSettled: (event, log) => {
console.log("Transfer settled:", event.newShieldHandle);
},
onDepositSettled: (event, log) => {
console.log("Deposit:", event.deposit.from, event.assetId, event.deposit.amount);
},
onWithdrawSettled: (event, log) => {
console.log("Withdraw:", event.withdraw.to, event.assetId, event.withdraw.amount);
},
onProxyAccountOperationSettled: (event, log) => {
console.log("Proxy op:", event.proxyAccount);
},
});
// Stop watching
unwatch();Parse individual event logs
import { contracts } from "@inco/shield-js";
const transferEvent = contracts.parseTransferSettledLog(log);
const depositEvent = contracts.parseDepositSettledLog(log);
const withdrawEvent = contracts.parseWithdrawSettledLog(log);
const proxyOpEvent = contracts.parseProxyAccountOperationSettledLog(log);Uniswap Adapter
Setup Clients
import { createPublicClient, createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { sepolia } from "viem/chains";
const account = privateKeyToAccount("0x...");
const publicClient = createPublicClient({
chain: sepolia,
transport: http("https://rpc.sepolia.org"),
});
const walletClient = createWalletClient({
account,
chain: sepolia,
transport: http("https://rpc.sepolia.org"),
});Authorize a DeFi Adapter
import { authorizeAdapter } from "@inco/shield-js";
const result = await authorizeAdapter({
publicClient,
walletClient,
incoShieldAddress: "0x...",
adapterAddress: "0x...",
});Find Uniswap V4 Pools
import { findPoolsWithLiquidity, UNISWAP_V4_SEPOLIA } from "@inco/shield-js";
const pools = await findPoolsWithLiquidity(
publicClient,
UNISWAP_V4_SEPOLIA.STATE_VIEW,
[
{
name: "ETH/USDC 0.3%",
currency0: "0x0000000000000000000000000000000000000000",
currency1: "0x...",
fee: 3000,
tickSpacing: 60,
hooks: "0x0000000000000000000000000000000000000000",
},
],
);Execute a Swap
import {
approveToken,
buildEncodedV4Actions,
encodeSwapArgs,
buildDefiInteraction,
depositToShield,
executeDefiInteraction,
generateNullifier,
getDefiInteractionDigest,
signDefiInteraction,
DEPLOYED_CONTRACTS,
SEPOLIA_TOKENS,
POOL_CONFIG,
} from "@inco/shield-js";
const {
incoShield: incoShieldAddress,
erc20Adapter: erc20AdapterAddress,
uniswapV4Adapter: uniswapAdapterAddress,
} = DEPLOYED_CONTRACTS;
const amount = parseUnits("0.1", 18);
// 1. Approve and deposit tokens to shield
await approveToken(walletClient, publicClient, SEPOLIA_TOKENS.WETH, incoShieldAddress, amount);
const { stateAfterDeposit } = await depositToShield(
walletClient,
publicClient,
incoShieldAddress,
{
toCiphertext: "0x01",
from: account.address,
assetAdapter: erc20AdapterAddress,
tokenAddress: SEPOLIA_TOKENS.WETH,
amount,
},
);
// 2. Build swap args
const poolKey = {
currency0: SEPOLIA_TOKENS.NATIVE_ETH,
currency1: SEPOLIA_TOKENS.USDC,
fee: POOL_CONFIG.FEE,
tickSpacing: POOL_CONFIG.TICK_SPACING,
hooks: POOL_CONFIG.HOOKS,
};
const encodedV4Actions = buildEncodedV4Actions({
poolKey,
zeroForOne: true,
amountIn: amount,
amountOutMinimum: 1n,
});
const swapArgs = encodeSwapArgs({
encodedV4Actions,
tokenIn: SEPOLIA_TOKENS.WETH,
tokenOut: SEPOLIA_TOKENS.USDC,
amountOutMinimum: 1n,
toCiphertext: "0x01",
deadline: BigInt(Math.floor(Date.now() / 1000) + 1800),
unwrapWeth: true,
});
// 3. Build and sign defi interaction
const defiInteraction = buildDefiInteraction({
to: uniswapAdapterAddress,
nullifier: generateNullifier(account.address),
pastShieldHandle: stateAfterDeposit,
assetAdapter: erc20AdapterAddress,
tokenAddress: SEPOLIA_TOKENS.WETH,
amount,
defiAdapter: uniswapAdapterAddress,
adapterArgs: swapArgs,
});
const digest = await getDefiInteractionDigest(publicClient, incoShieldAddress, defiInteraction);
const signature = await signDefiInteraction(account, digest);
// 4. Execute
const { txHash, blockNumber } = await executeDefiInteraction(
walletClient,
publicClient,
incoShieldAddress,
defiInteraction,
signature,
{ gas: 1_000_000n },
);Development
bun install
bun run generate # Generate proto types + ABIs
bun run build # Generate + compile TypeScript
bun run test # Run tests
bun run typecheck # Type-check only
bun run uniswap # Run example swap script