salt-sdk
v0.0.37
Published
SDK for interacting with Salt's MPC self custody & treasury
Readme
Salt SDK
TypeScript client for Salt, an open MPC self-custodial infrastructure for organisations. With Salt, anyone can spin up a system of self-sovereignty for self-custodial wealth management, including delegations to 3rd parties such as asset managers, robo-advisors or agents.
⚠️ Pre-release software. Before upgrading:
- Accounts created with this version are incompatible with previous versions.
- Testnet accounts will be wiped in an upcoming release - do not treat them as long-term.
- Mainnet is not yet supported — this version creates testnet accounts only.
Documentation
Install
npm install salt-sdk viemviem is a peer dependency.
Example
import { Salt } from 'salt-sdk';
import {
createPublicClient,
createWalletClient,
http,
parseEther,
type Hex,
} from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { arbitrumSepolia, sepolia } from 'viem/chains';
// Create an instance — TESTNET uses Arbitrum Sepolia for orchestration
const salt = new Salt({ environment: 'TESTNET' });
// Set up a viem wallet client (orchestration chain must match the environment)
const account = privateKeyToAccount(process.env.PRIVATE_KEY as Hex);
const walletClient = createWalletClient({
account,
chain: arbitrumSepolia,
transport: http(),
});
// Authenticate with SIWE
await salt.authenticate(walletClient);
// Fetch your organisations and accounts
const orgs = await salt.getOrganisations();
const accounts = await salt.getAccounts(orgs[0].id);
// Submit a native ETH transfer to Sepolia
const publicClient = createPublicClient({
chain: sepolia,
transport: http(process.env.SEPOLIA_RPC_URL),
});
const ceremony = await salt.submitTx({
accountId: accounts[0].id,
to: '0x000000000000000000000000000000000000dEaD',
value: parseEther('0.01'),
chainId: 11155111,
userAddress: account.address,
walletClient,
publicClient,
});
// Track progress: proposing → signing → broadcasting → confirming → success | failure
ceremony.on('stateChanged', ({ stage }) => console.log('tx is now', stage));
// Wait for MPC signing + broadcast to complete
const { transaction } = await ceremony.wait();
console.log('tx hash:', transaction.txHash);See More: Salt constructor · authenticate · getOrganisations · getAccounts · submitTx
Two-network model
Every Salt transaction involves two networks:
- Orchestration network — where MPC signing coordination happens. Set by the
environment:STAGING&TESTNET= Arbitrum SepoliaMAINNET= Arbitrum One
- Destination network — where the transaction actually executes. Set by
chainId+publicClientinsubmitTx. Can be any supported EVM chain.
Tracking progress
const ceremony = await salt.submitTx({
/* ... */
});
ceremony.on('stateChanged', ({ stage }) => {
console.log('transaction is now', stage);
// proposing → signing → broadcasting → confirming → success | failure
});
const { transaction } = await ceremony.wait();See More: submitTx · TransactionHostCeremony
Policies
Policies control which transactions robo guardians will co-sign. Create them per account and chain. If a transaction violates a policy, the robos refuse to sign and it fails before broadcast.
// Restrict ERC-20 approve() calls: only allow a specific spender, cap the amount
await salt.createAccountPolicy({
accountId: accounts[0].id,
organisationId: orgs[0].id,
type: 'contract_param_restriction',
chain: '11155111', // Sepolia
params: {
restrictions: [
{
contractAddress: '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238', // USDC on Sepolia
functionSignature: 'approve(address,uint256)',
paramIndex: 0, // spender argument
operator: 'eq',
value: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
},
{
contractAddress: '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238',
functionSignature: 'approve(address,uint256)',
paramIndex: 1, // amount argument
operator: 'lte',
value: '1000000', // 1 USDC (6 decimals)
},
],
},
});See More: createAccountPolicy · ContractParamRestriction · PolicyParams
Contract calls
Pass encoded calldata via data. The transaction below is valid against the contract_param_restriction policy above — the spender matches and the amount is within the cap.
import { encodeFunctionData, parseAbi } from 'viem';
const data = encodeFunctionData({
abi: parseAbi(['function approve(address spender, uint256 amount)']),
functionName: 'approve',
args: [
'0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045', // allowed spender
500_000n, // 0.5 USDC — within the 1 USDC cap
],
});
const ceremony = await salt.submitTx({
accountId: accounts[0].id,
to: '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238', // USDC on Sepolia
value: 0n,
chainId: 11155111,
data,
userAddress: account.address,
walletClient,
publicClient,
});
await ceremony.wait();See More: submitTx · SendTransactionParams
Organisations and accounts
Organisations group collaborators and own accounts. Accounts are MPC wallets: humans create them via a key-generation ceremony and become signers. Fetch what the authenticated user belongs to with getOrganisations and getAccounts, create new ones with createOrganisation and createAccount, and manage collaborators with inviteCollaborator.
See examples: createOrganisation · inviteCollaborator · updateCollaborator · createAccount · getOrganisations · getAccounts
Robos
Robos belong to an organisation, and are automated co-signers for accounts. Manage robo hosts with createRoboHost and getRoboHost.
See examples: Salt constructor · createRoboHost · getRoboHost · RoboHost
Full API reference and more examples at kagamidigital.github.io/salt-sdk-mirror.
