Type-safe contract interfaces, functions, and React hooks for Clarity smart contracts.
bun add -g @secondlayer/cliGenerate from local files or deployed contracts—no config required:
# Local .clar files
secondlayer generate ./contracts/token.clar -o ./src/generated.ts
# Deployed contracts (network inferred from address)
secondlayer generate SP3K8BC0PPEVCV7NZ6QSRWPQ2JE9E5B6N3PA0KBR9.alex-vault -o ./src/generated.ts
# Glob patterns
secondlayer generate "./contracts/*.clar" -o ./src/generated.tssecondlayer init # creates secondlayer.config.ts
secondlayer generate// secondlayer.config.ts
import { defineConfig } from '@secondlayer/cli'
import { clarinet, actions, react } from '@secondlayer/cli/plugins'
export default defineConfig({
out: 'src/generated.ts',
plugins: [
clarinet(), // parse local Clarinet project
actions(), // add read/write helpers
react(), // generate React hooks
],
})import { token } from './generated/contracts'
import { makeContractCall, fetchCallReadOnlyFunction } from '@stacks/transactions'
// works with @stacks/transactions directly
await makeContractCall({
...token.transfer({ amount: 100n, recipient: "SP..." }),
network: 'mainnet',
})
await fetchCallReadOnlyFunction({
...token.getBalance({ account: "SP..." }),
network: 'mainnet',
})Requires actions() plugin:
// read-only
const balance = await token.read.getBalance({ account: "SP..." })
// write (uses STX_SENDER_KEY env var)
await token.write.transfer({ amount: 100n, recipient: "SP..." })
// or pass senderKey explicitly
await token.write.transfer({ amount: 100n, recipient: "SP..." }, "<sender-key>")
// with additional options
await token.write.transfer({ amount: 100n, recipient: "SP..." }, "<sender-key>", { network: 'testnet' })Access maps, variables, constants directly:
// maps
const balance = await token.maps.balances.get("SP...")
// variables
const supply = await token.vars.totalSupply.get()
// constants
const max = await token.constants.maxSupply.get()
// network override
const devBalance = await token.maps.balances.get("SP...", { network: 'devnet' })Requires react() plugin:
import { useTokenTransfer, useTokenBalances, useTokenTotalSupply } from './generated/hooks'
function App() {
const { transfer, isRequestPending } = useTokenTransfer()
const { data: balance } = useTokenBalances("SP...")
const { data: supply } = useTokenTotalSupply()
return (
<button onClick={() => transfer({ amount: 100n, recipient: "SP..." })} disabled={isRequestPending}>
Transfer
</button>
)
}Requires testing() plugin:
import { getContracts } from './helpers'
const simnet = await initSimnet()
const { token } = getContracts(simnet)
// call functions
const result = token.transfer({ amount: 100n, recipient: "ST..." }, "wallet_1")
expect(result.result).toBeOk(Cl.bool(true))
// read state
const supply = token.vars.totalSupply()
const balance = token.maps.balances("ST...")| Plugin | Description |
|---|---|
clarinet() |
Parse local Clarinet project |
actions() |
Add read/write helpers |
react() |
Generate React hooks |
testing() |
Generate Clarinet SDK test helpers |
Address prefix determines network:
SP/SM→ mainnetST/SN→ testnet
MIT