完整文档页面(中文翻译)。文末附有来源说明。

阅读英文版

integrationswalletwallet-sdk-v1-migration

Wallet SDK v1 迁移

Migrate 钱包 SDK 集成 from v0 to v1: 配置, namespaces, and per-namespace changes.

钱包 SDK v1 Migration Guide

钱包 SDK v1 is not backwards compatible with v0.

Quick links

  • 钱包-sdk-config - Detailed 配置 guide
  • preparing-and-signing-交易 - Preparing and signing 交易

We have removed the configure() and connect() pattern in favor of passing in a static 配置 or a 提供方 with ledger api capabilities.

Static 配置 initialization where we supply an auth config and a ledgerClientUrl:

import { SDK, localNetStaticConfig } from '@canton-network/wallet-sdk'

export default async function () {
    const sdk = await SDK.create({
        auth: {
            method: 'self_signed',
            issuer: 'unsafe-auth',
            credentials: {
                clientId: 'ledger-api-user',
                clientSecret: 'unsafe',
                audience: 'https://canton.network.global',
                scope: '',
            },
        },
        ledgerClientUrl: new URL('http://localhost:2975'),
        token: {
            validatorUrl: new URL('http://localhost:2000/api/validator'),
            registries: [
                new URL('http://localhost:2000/api/validator/v0/scan-proxy'),
            ],
            auth: global.TOKEN_PROVIDER_CONFIG_DEFAULT,
        },
        amulet: {
            validatorUrl: localNetStaticConfig.LOCALNET_APP_VALIDATOR_URL,
            scanApiUrl: localNetStaticConfig.LOCALNET_SCAN_API_URL,
            auth: TOKEN_PROVIDER_CONFIG_DEFAULT,
            registryUrl: localNetStaticConfig.LOCALNET_REGISTRY_API_URL,
        },
        asset: {
            registries: [localNetStaticConfig.LOCALNET_REGISTRY_API_URL],
            auth: TOKEN_PROVIDER_CONFIG_DEFAULT,
        },
    })

    const myParty = global.EXISTING_PARTY_1

    await sdk.token.utxos.list({ partyId: myParty })

    await sdk.amulet.traffic.status()

    // OR, you can defer loading config by calling .extend()

    const basicSDK = await SDK.create({
        auth: {
            method: 'self_signed',
            issuer: 'unsafe-auth',
            credentials: {
                clientId: 'ledger-api-user',
                clientSecret: 'unsafe',
                audience: 'https://canton.network.global',
                scope: '',
            },
        },
        ledgerClientUrl: new URL('http://localhost:2975'),
    })

    // Extend with token namespace
    const tokenExtendedSDK = await basicSDK.extend({
        token: {
            validatorUrl: new URL('http://localhost:2000/api/validator'),
            registries: [
                new URL('http://localhost:2000/api/validator/v0/scan-proxy'),
            ],
            auth: global.TOKEN_PROVIDER_CONFIG_DEFAULT,
        },
    })

    // Now token namespace is available
    await tokenExtendedSDK.token.utxos.list({ partyId: myParty })

    // Can extend further with more namespaces
    const fullyExtendedSDK = await tokenExtendedSDK.extend({
        amulet: {
            validatorUrl: localNetStaticConfig.LOCALNET_APP_VALIDATOR_URL,
            scanApiUrl: localNetStaticConfig.LOCALNET_SCAN_API_URL,
            auth: global.TOKEN_PROVIDER_CONFIG_DEFAULT,
            registryUrl: localNetStaticConfig.LOCALNET_REGISTRY_API_URL,
        },
    })

    // Now both token and amulet are available
    await fullyExtendedSDK.token.utxos.list({ partyId: myParty })
    await fullyExtendedSDK.amulet.traffic.status()
}

提供方 intialization: The 提供方 is an abstraction that ultimately interacts with the Ledger (JSON LAPI). This can be implemented for either a dApp consumer, direct ledger 用户, or alternative transport channels such as 钱包 连接.

// Notice that `auth` and `ledgerClientUrl` are no longer needed
// when supplying sdk with custom provider
const sdk = await SDK.create(config, provider)

Namespace changes

We have removed the controllers and replaced them with namespaces to appropriately segregate the 服务 layer in terms of business context. When the sdk is initialized, it has access to the 用户, keys, ledger, and party namespaces. The amulet, token, asset, and 事件 namespace can initialized with a separate config via .extend() 方法.

Removed functionality

以下 方法 have been removed:

sdk.connect() No longer needed, SDK is connected on creation sdk.connectAdmin() No longer needed, admin operations are available in the ledger namespace and rights are extracted from the token. sdk.connectTopology() No longer needed, the grpc 端点 have been removed and replaced with ledger api 端点. sdk.setPartyId() Pass partyId explicitly to each operation

```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}} sdk.setPartyId(myPartyId) const holdingTransactionsmyPartyId = await sdk.tokenStandard?.listHoldingTransactions() sdk.setPartyId(myPartyId2) const holdingTransactionsmyPartyId2 = await sdk.tokenStandard?.listHoldingTransactions() ```
const holdingTransactionsmyPartyId = await token.holdings(myPartyId)
const holdingTransactionsmyPartyId2 = await token.holdings(myPartyId2)

In v0, the controllers and sdk were stateful. In v1, party information should be passed explicitly to each function. This enables acting as multiple Party and allows for thread safety in concurrent use.

Migration reference table

v0 controller + 方法v1 namespace + 方法
createKeyPair()sdk.keys.generate()
sdk.userLedger.signAndAllocateExternalParty(privateKey, partyHint)sdk.party.external.create(publicKey, {partyHint}).sign(privateKey).execute()
sdk.userLedger.listWallets()sdk.party.list()
sdk.userLedger.prepareSignExecuteAndWaitForsdk.ledger.prepare({partyId, 命令, disclosedContracts}).sign(privateKey).execute(partyId)
sdk.userLedger.activeContractssdk.ledger.acs.read
sdk.adminLedger.uploadDarsdk.ledger.dar.upload
sdk.userLedger.isPackageUploadedsdk.ledger.dar.check
sdk.adminLedger.createUsersdk.用户.create
sdk.userLedger.grantRightssdk.用户.rights.grant
sdk.tokenStandard.createTransfersdk.token.transfer.create
sdk.tokenStandard.exerciseTransferInstructionChoicesdk.token.transfer.accept / sdk.token.transfer.reject / sdk.token.transfer.withdraw
sdk.tokenStandard.fetchPendingTransferInstructionViewsdk.token.transfer.pending
sdk.tokenStandard.listHoldingTransactions({partyId})sdk.token.持仓
sdk.tokenStandard.listHoldingUtxos()sdk.token.utxos.list({partyId})
sdk.tokenStandard.mergeHoldingUtxossdk.token.utxos.merge
sdk.tokenStandard.fetchPendingAllocationRequestViewsdk.token.allocation.pending(partyId, ALLOCATION_REQUEST_INTERFACE_ID)
sdk.tokenStandard.fetchPendingAllocationInstructionViewsdk.token.allocation.pending(partyId, ALLOCATION_INSTRUCTION_INTERFACE_ID)
sdk.tokenStandard.fetchPendingAllocationViewsdk.token.allocation.pending(partyId)
sdk.tokenStandard.getAllocationExecuteTransferChoiceContext(cId)sdk.token.allocation.context.execute
sdk.tokenStandard.getAllocationWithdrawChoiceContext(cId)sdk.token.allocation.context.withdraw
sdk.tokenStandard.getAllocationCancelChoiceContext(cId)sdk.token.allocation.context.cancel
sdk.tokenStandard.getMemberTrafficStatussdk.amulet.流量.status
sdk.tokenStandard.buyMemberTrafficsdk.amulet.流量.buy
sdk.userLedger.createTransferPreapprovalCommandsdk.amulet.preapproval.命令.create
sdk.tokenStandard.getTransferPreApprovalByPartysdk.amulet.preapproval.fetchStatus
sdk.tokenStandard.createRenewTransferPreapprovalsdk.amulet.preapproval.renew
sdk.tokenStandard.createCancelTransferPreapprovalsdk.amulet.preapproval.命令.cancel
sdk.tokenStandard.createTapsdk.amulet.tap
sdk.tokenStandard.lookupFeaturedAppssdk.amulet.featuredApp.rights
sdk.tokenStandard.selfGrantFeatureAppRightssdk.amulet.featuredApp.grant
sdk.tokenStandard.getInstrumentByIdsdk.asset.find
sdk.tokenStandard.listInstrumentssdk.asset.list
sdk.userLedger.subscribeToUpdatessdk.事件.updates
sdk.userLedger.subscribeToCompletionssdk.事件.completions

Migration reference table

{/* COPIED_START source=“splice-钱包-kernel:docs/钱包-集成-guide/src/钱包-sdk-v1-migration-guide/party.rst” hash=“bc376f31” */}

Party Namespace

The party namespace provides 方法 to manage 钱包 Party on the Canton 网络. In v1, the party namespace replaces the stateful party management from v0.

Availability

The party namespace is always available as part of the basic SDK interface. It’s initialized automatically when you create an SDK instance and doesn’t require additional 配置 via extend().

import { SDK, localNetStaticConfig } from '@canton-network/wallet-sdk'

export default async function () {
    const sdk = await SDK.create({
        auth: global.TOKEN_PROVIDER_CONFIG_DEFAULT,
        ledgerClientUrl: localNetStaticConfig.LOCALNET_APP_USER_LEDGER_URL,
    })

    // party namespace is immediately available
    await sdk.party.list()
}

Key changes from v0 to v1

v0 used a stateful approach where you set a party context once with sdk.setPartyId(). All subsequent operations acted on that party.

v1 uses an explicit approach where you pass the party ID to each operation. This enables:

  • Thread-safe concurrent operations
  • Multi-party 交易 within the same 应用
  • Clearer code intent
```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}} sdk.setPartyId(myPartyId) const result = await sdk.userLedger.doSomething() ```
const result = await sdk.ledger
    .prepare({ partyId: myPartyId, ... })
    .sign(privateKey)
    .execute({ partyId: myPartyId })

Refer to preparing-and-signing-交易 for more information.

Party types

Internal Party

```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}} const internalParty = await sdk.adminLedger!.allocateInternalParty(partyHint) ```
// v1 - no state, explicit party ID
const internalParty = await sdk.party.internal.allocate({
    partyHint: 'my-service',
    synchronizerId: 'my-synchronizer-id'
})

The below example demonstrates the full usage of the feature:

我们建议 always providing a `partyHint` when creating a party. Refer to `party-hint` for more details.

Listing Party

```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}} const wallets = await sdk.userLedger?.listWallets() ```
const partyIds = await sdk.party.list()

This 方法 returns all Party where the 用户 has CanActAs, CanReadAs, or CanExecuteAs rights. 若 用户 has admin rights, all local Party are returned.

Offline signing 工作流

```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}} const preparedParty = await sdk.userLedger?.generateExternalParty( keyPair.publicKey, partyHint ) const allocatedParty = await sdk.userLedger?.allocateExternalParty( signature, preparedParty ) ```
const party = await sdk.party.external
    .create(publicKey, options)
    .execute(signature, executeOptions)

Migration reference

v0 方法v1 方法
sdk.setPartyId(partyId)Pass partyId explicitly to each operation
sdk.userLedger.listWallets()sdk.party.list()
sdk.userLedger.signAndAllocateExternalParty(privateKey, partyHint)sdk.party.external.create(publicKey, {partyHint}).sign(privateKey).execute()
sdk.topology?.prepareExternalPartyTopology()sdk.party.external.create().prepare() (implicit on create)
sdk.topology?.submitExternalPartyTopology()sdk.party.external.create().sign().execute()

Party-related 方法 migration

See also

  • 钱包-sdk-config - SDK 配置
  • preparing-and-signing-交易 - 交易 lifecycle

{/* COPIED_START source=“splice-钱包-kernel:docs/钱包-集成-guide/src/钱包-sdk-v1-migration-guide/token.rst” hash=“7742b4ca” */}

Token Namespace

The token namespace provides 方法 to manage token operations including transfers, 持仓, UTXOs, and allocations on the Canton 网络. In v1, the token namespace replaces the tokenStandard controller from v0.

Availability and extensibility

The token namespace is an extended namespace that requires 配置. 你可以 initialize it either during SDK creation or later using the extend() 方法.

Option 1: Initialize during SDK creation

import { SDK, localNetStaticConfig } from '@canton-network/wallet-sdk'

export default async function () {
    const sdk = await SDK.create({
        auth: global.TOKEN_PROVIDER_CONFIG_DEFAULT,
        ledgerClientUrl: localNetStaticConfig.LOCALNET_APP_USER_LEDGER_URL,
        token: global.TOKEN_NAMESPACE_CONFIG,
    })

    const partyId = EXISTING_PARTY_1

    // token namespace is now available
    await sdk.token.utxos.list({ partyId })
}

Option 2: 添加 token namespace later using extend()

import { SDK, localNetStaticConfig } from '@canton-network/wallet-sdk'

export default async function () {
    // Create basic SDK first
    const basicSDK = await SDK.create({
        auth: global.TOKEN_PROVIDER_CONFIG_DEFAULT,
        ledgerClientUrl: localNetStaticConfig.LOCALNET_APP_USER_LEDGER_URL,
    })

    // Extend with token namespace when needed
    const extendedSDK = await basicSDK.extend({
        token: global.TOKEN_NAMESPACE_CONFIG,
    })

    const partyId = EXISTING_PARTY_1

    // Now token namespace is available
    await extendedSDK.token.utxos.list({ partyId })
}

Key changes from v0 to v1

v0 used the tokenStandard controller with implicit party context set via sdk.setPartyId().

v1 uses the token namespace where you:

  • Pass partyId explicitly to each operation
  • Initialize the namespace with 配置
  • Access operations through logical groupings (transfer, utxos, allocation)
```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}} sdk.setPartyId(myPartyId) const holdings = await sdk.tokenStandard?.listHoldingTransactions() ```
const holdings = await sdk.token.holdings({ partyId: myPartyId })

This enables thread-safe concurrent operations and clearer code organization.

Transfers

Creating transfers

```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}} const transfer = await sdk.tokenStandard.createTransfer( senderPartyId, recipientPartyId, amount.toString(), { instrumentId: 'Amulet', instrumentAdmin: instrumentAdminPartyId }, registryUrl, memo ) ```
const [command, disclosedContracts] = await sdk.token.transfer.create({
    sender: senderPartyId,
    recipient: recipientPartyId,
    amount: amount.toString(),
    instrumentId: 'Amulet',
    registryUrl,
    inputUtxos: ['utxo-1', 'utxo-2'],
    memo: 'Payment for services',
})

Accepting, rejecting, or withdrawing transfers

```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}} await sdk.tokenStandard.exerciseTransferInstructionChoice(transferCid, choiceType /* 'Accept' | 'Withdraw' | 'Reject' */) ```
// Accept transfer
const [acceptCommand, disclosed1] = await sdk.token.transfer.accept({
    transferInstructionCid,
    registryUrl,
})

// Reject transfer
const [rejectCommand, disclosed2] = await sdk.token.transfer.reject({
    transferInstructionCid,
    registryUrl,
})

// Withdraw transfer
const [withdrawCommand, disclosed3] = await sdk.token.transfer.withdraw({
    transferInstructionCid,
    registryUrl,
})

Listing pending transfers

```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}} await sdk.setPartyId(partyId) const pending = await sdk.tokenStandard .fetchPendingTransferInstructionView() ```
const pending = await sdk.token.transfer.pending(myPartyId)

持仓

持仓 represent the 交易 history of token ownership for a party.

```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}} await sdk.setPartyId(partyId) const holdings = await sdk.tokenStandard .listHoldingTransactions() ```
const holdings = await sdk.token.holdings({ partyId })

你可以 also specify 偏移 for pagination:

const holdings = await sdk.token.holdings({
    partyId,
    afterOffset: 10,
    beforeOffset: 100,
})

交易 by updateId:

```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}} await sdk.setPartyId(partyId) const tx = await sdk.tokenStandard.getTransactionById('my-update-id') ```
const tx = await sdk.token.transactionsById({ updateId, partyId })

UTXOs

UTXOs (Unspent 交易 Outputs) are the actual holding 合约 that represent token balances.

Listing UTXOs

```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}} await sdk.setPartyId(partyId) // List all UTXOs including locked ones const allUtxos = await sdk.tokenStandard?.listHoldingUtxos()

// List only unlocked UTXOs const usableUtxos = await sdk.tokenStandard?.listHoldingUtxos(false)


***

```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}}
// List only unlocked UTXOs (default)
const usableUtxos = await sdk.token.utxos.list({
    partyId
})

// List all UTXOs including locked ones
const allUtxos = await sdk.token.utxos.list({
    partyId,
    includeLocked: true,
})

你可以 specify additional parameters for pagination and limits:

const utxos = await sdk.token.utxos.list({
    partyId,
    includeLocked: false,
    limit: 100,
    offset: 0,
    continueUntilCompletion: false,
})

Merging UTXOs

Merging consolidates multiple small UTXOs into larger ones to improve performance.

```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}} await sdk.setPartyId(partyId) const [commands, disclosedContracts] = await sdk.tokenStandard.mergeHoldingUtxos(nodeLimit) ```
const [commands, disclosedContracts] = await sdk.token.utxos.merge({
    partyId,
    nodeLimit: 200,
    memo: 'merge-utxos',
})

The merge operation groups UTXOs by instrument and creates self-transfers to consolidate them. 你可以 optionally provide specific UTXOs to merge:

const [commands, disclosedContracts] = await sdk.token.utxos.merge({
    partyId,
    inputUtxos,
    memo: 'custom merge',
})

Allocation

Allocations handle the issuance and distribution of new tokens.

Listing pending allocations

```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}} await sdk.setPartyId(partyId) // Allocation requests const requests = await sdk.tokenStandard .fetchPendingAllocationRequestView()

// Allocation instructions const instructions = await sdk.tokenStandard .fetchPendingAllocationInstructionView()

// Allocations const allocations = await sdk.tokenStandard .fetchPendingAllocationView()


***

```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}}
// All pending allocations (default)
const allocations = await sdk.token.allocation.pending(myPartyId)

The pending 方法 accepts an optional interface ID to filter by allocation type:

import {
    ALLOCATION_REQUEST_INTERFACE_ID,
    ALLOCATION_INSTRUCTION_INTERFACE_ID,
    ALLOCATION_INTERFACE_ID,
} from '@canton-network/core-token-standard'

// Filter by specific type
const requests = await sdk.token.allocation.pending(
    myPartyId,
    ALLOCATION_REQUEST_INTERFACE_ID
)

Executing, withdrawing or cancelling allocations

```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}} const [command, disclosedContracts] = await sdk.tokenStandard.exerciseAllocationChoice(allocationCid, choice /* 'ExecuteTransfer' | 'Withdraw' | 'Cancel' */) ```
// Execute allocation
const [executeCommand, disclosedContracts1] = await sdk.token.allocation.execute({
    allocationCid,
    asset
})

 // Withdraw allocation
const [withdrawCommand, disclosedContracts2] = await sdk.token.allocation.withdraw({
    allocationCid,
    asset,
})

// Cancel allocation
const [cancelCommand, disclosedContracts3] = await sdk.token.allocation.cancel({
    allocationCid,
    asset,
})

Migration reference

v0 方法v1 方法
sdk.tokenStandard.createTransfersdk.token.transfer.create
sdk.tokenStandard.exerciseTransferInstructionChoicesdk.token.transfer.accept / sdk.token.transfer.reject / sdk.token.transfer.withdraw
sdk.tokenStandard.fetchPendingTransferInstructionViewsdk.token.transfer.pending
sdk.tokenStandard.listHoldingTransactions({partyId})sdk.token.持仓({partyId})
sdk.tokenStandard.listHoldingUtxos()sdk.token.utxos.list({partyId})
sdk.tokenStandard.mergeHoldingUtxossdk.token.utxos.merge
sdk.tokenStandard.fetchPendingAllocationRequestViewsdk.token.allocation.pending(partyId, ALLOCATION_REQUEST_INTERFACE_ID)
sdk.tokenStandard.fetchPendingAllocationInstructionViewsdk.token.allocation.pending(partyId, ALLOCATION_INSTRUCTION_INTERFACE_ID)
sdk.tokenStandard.fetchPendingAllocationViewsdk.token.allocation.pending(partyId)

Token-related 方法 migration

See also

  • 钱包-sdk-config - SDK 配置
  • preparing-and-signing-交易 - 交易 lifecycle

{/* COPIED_START source=“splice-钱包-kernel:docs/钱包-集成-guide/src/钱包-sdk-v1-migration-guide/用户.rst” hash=“7cf5184f” */}

用户 Namespace

The 用户 namespace provides 方法 for 用户 management on the Canton 网络.

Availability

The 用户 namespace is always available as part of the basic SDK interface. It’s initialized automatically when you create an SDK instance and doesn’t require additional 配置 via extend().

import { localNetStaticConfig, SDK } from '@canton-network/wallet-sdk'

export default async function () {
    const sdk = await SDK.create({
        auth: TOKEN_PROVIDER_CONFIG_DEFAULT,
        ledgerClientUrl: localNetStaticConfig.LOCALNET_APP_USER_LEDGER_URL,
    })

    const primaryParty = EXISTING_PARTY_1
    const userId = 'user-id'

    // user namespace is immediately available
    await sdk.user.create({ userId, primaryParty })
}

Key changes from v0 to v1

The distinction betwen the 用户 ledger and admin ledger have been removed. Instead, the token is used to determine whether a 用户 has admin rights.

Creating 用户

```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}} await sdk.adminLedger.createUser('userId', primaryParty) ```
const result = await sdk.user
    .create({
      userId: 'userId',
      primaryParty: primaryParty,
      userRights: {...}
    })

Granting 用户 rights

```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}} await sdk.userLedger.grantRights([actAsRights], [readAsRights]) ```
const result = await sdk.rights
    .grant({
      userRights: {...},
      userId: {'userId'}, //optional parameter
      idp: {'idp'} //optional parameter otherwise will use default IDP
    })

The below example demonstrates the full usage of the feature: