> ## Documentation Index
> Fetch the complete documentation index at: https://docs.canton.network/llms.txt
> Use this file to discover all available pages before exploring further.

# Amulet Namespace

> Migrate Canton Coin, traffic, preapprovals, and tap from v0 to v1.

The amulet namespace is used for Canton coin specific operations.

## Availability and extensibility

The amulet namespace is an extended namespace that requires configuration. You can initialize it either during SDK creation or later using the `extend()` method.

**Option 1: Initialize during SDK creation**

```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}}
const sdk = await SDK.create({
    auth: authConfig,
    ledgerClientUrl: 'http://localhost:2975',
    amulet: {
        validatorUrl: 'http://localhost:2000/api/validator',
        scanApiUrl: 'http://localhost:2000/api/scan',
        auth: amuletAuthConfig,
        registryUrl: 'http://localhost:2000/api/registry'
    }
})

// amulet namespace is now available
await sdk.amulet.traffic.status()
```

**Option 2: Add amulet namespace later using extend()**

```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}}
// Create basic SDK first
const basicSDK = await SDK.create({
    auth: authConfig,
    ledgerClientUrl: 'http://localhost:2975'
})

// Extend with amulet namespace when needed
const extendedSDK = await basicSDK.extend({
    amulet: {
        validatorUrl: 'http://localhost:2000/api/validator',
        scanApiUrl: 'http://localhost:2000/api/scan',
        auth: amuletAuthConfig,
        registryUrl: 'http://localhost:2000/api/registry'
    }
})

// Now amulet namespace is available
await extendedSDK.amulet.traffic.status()
```

## Configuration

The `AmuletConfig` type defines the configuration for the amulet namespace:

```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
type AmuletConfig = {
    auth: TokenProviderConfig
    validatorUrl: string | URL
    scanApiUrl: string | URL
    registryUrl: URL
}
```

* `auth`: Authentication configuration for accessing the validator and scan services
* `validatorUrl`: URL of the validator service
* `scanApiUrl`: URL of the scan API
* `registryUrl`: URL of the amulet registry

## Key changes from v0 to v1

v0 used the `tokenStandard` controller with implicit party context set via `sdk.setPartyId()` and the instrumentId and instrumentAdmin were passed in explicitly to each function.

v1 uses the `amulet` namespace where you:

* Pass `partyId` explicitly to each operation
* Initialize the namespace with configuration, which determines the instrumentAdmin and instrumentId
* Access operations through logical groupings (`traffic` and `preapproval`)

**Creating preapprovals**

<div className="before-after">
  ```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}}
  const transferPreApprovalProposal =
     await sdk.userLedger?.createTransferPreapprovalCommand(
        validatorOperatorParty!,
        receiver?.partyId!,
        instrumentAdminPartyId
     )

  await sdk.userLedger?.prepareSignExecuteAndWaitFor(
     [transferPreApprovalProposal],
     keyPairReceiver.privateKey,
     v4()
  )
  ```

  ***

  ```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}}
  const createPreapprovalCommand = await sdk.amulet.preapproval.command.create({
     parties: {
        receiver: partyId,
     },
  })

  await sdk.ledger
        .prepare({
           partyId: partyId,
           commands: createPreapprovalCommand,
        })
        .sign(privateKey)
        .execute({
           partyId: partyId,
        })
  ```
</div>

The below example demonstrates the full process of renewing and cancelling preapprovals:

```javascript title="Full example: create, renew, and cancel preapprovals" expandable theme={"theme":{"light":"github-light","dark":"github-dark"}}
import { Holding, PrettyContract } from '@canton-network/core-tx-parser'
import { localNetStaticConfig, SDK } from '@canton-network/wallet-sdk'
import { pino } from 'pino'
import {
    TOKEN_NAMESPACE_CONFIG,
    TOKEN_PROVIDER_CONFIG_DEFAULT,
    AMULET_NAMESPACE_CONFIG,
} from './utils/index.js'

const logger = pino({ name: 'v1-05-preapproval', level: 'info' })

const sdk = await SDK.create({
    auth: TOKEN_PROVIDER_CONFIG_DEFAULT,
    ledgerClientUrl: localNetStaticConfig.LOCALNET_APP_USER_LEDGER_URL,
    token: TOKEN_NAMESPACE_CONFIG,
    amulet: AMULET_NAMESPACE_CONFIG,
})

await sdk.amulet.tapInternal('1000')

const aliceKeys = sdk.keys.generate()

const alice = await sdk.party.external
    .create(aliceKeys.publicKey, {
        partyHint: 'v1-05-alice',
    })
    .sign(aliceKeys.privateKey)
    .execute()

const [amuletTapCommand, amuletTapDisclosedContracts] = await sdk.amulet.tap(
    alice.partyId,
    '10000'
)

await sdk.ledger
    .prepare({
        partyId: alice.partyId,
        commands: amuletTapCommand,
        disclosedContracts: amuletTapDisclosedContracts,
    })
    .sign(aliceKeys.privateKey)
    .execute({ partyId: alice.partyId })

const bobKeys = sdk.keys.generate()

const bob = await sdk.party.external
    .create(bobKeys.publicKey, {
        partyHint: 'v1-05-bob',
    })
    .sign(bobKeys.privateKey)
    .execute()

// --- TEST CREATE COMMAND

const createPreapprovalCommand = await sdk.amulet.preapproval.command.create({
    parties: {
        receiver: bob.partyId,
    },
})

logger.info(
    { createPreapprovalCommand },
    'Successfully created a preapproval command'
)

await sdk.ledger
    .prepare({
        partyId: bob.partyId,
        commands: createPreapprovalCommand,
    })
    .sign(bobKeys.privateKey)
    .execute({
        partyId: bob.partyId,
    })

logger.info('Successfully registered the preapproval.')

// --- TEST FETCH

const start = performance.now()
const fetchOnceStatus = await sdk.amulet.preapproval.fetchQuick(bob.partyId)
const end = performance.now()

const duration = end - start
if (duration < 1000) {
    logger.info(
        `Success! The operation was fast (${duration.toFixed(2)} ms) and fetchOnce status is ${fetchOnceStatus}.`
    )
} else {
    logger.warn(
        `Warning: Operation took longer than 1 second (${(duration / 1000).toFixed(2)} s).`
    )
}

logger.info('Fetching for preapproval status with retry')

const fetchedPreapprovalStatus = await sdk.amulet.preapproval.fetchStatus(
    bob.partyId
)

logger.info({ fetchedPreapprovalStatus }, 'Fetched preapproval status')

const sentValue = 2000

const [transferCommand, transferDisclosedContracts] =
    await sdk.token.transfer.create({
        sender: alice.partyId,
        recipient: bob.partyId,
        amount: sentValue.toString(),
        instrumentId: 'Amulet',
        registryUrl: localNetStaticConfig.LOCALNET_REGISTRY_API_URL,
    })

await sdk.ledger
    .prepare({
        partyId: alice.partyId,
        commands: transferCommand,
        disclosedContracts: transferDisclosedContracts,
    })
    .sign(aliceKeys.privateKey)
    .execute({ partyId: alice.partyId })

logger.info({ sentValue }, 'Executed transfer from Alice to Bob with value:')

const aliceUtxos = await sdk.token.utxos.list({ partyId: alice.partyId })
const bobUtxos = await sdk.token.utxos.list({ partyId: bob.partyId })

const partyAmuletValue = (utxos: PrettyContract<Holding>[]) =>
    utxos.reduce(
        (acc, utxo) => acc + parseFloat(utxo.interfaceViewValue.amount),
        0
    )
const aliceAmuletValue = partyAmuletValue(aliceUtxos)
const bobAmuletValue = partyAmuletValue(bobUtxos)

if (aliceAmuletValue !== 8000 || bobAmuletValue !== 2000)
    throw Error(
        `Wrong end results for utxos: ${JSON.stringify({ aliceAmuletValue, bobAmuletValue })}`
    )

logger.info({ aliceAmuletValue, bobAmuletValue }, 'Result:')

// --- TEST RENEW COMMAND

logger.info('Renewing preapproval...')

const start2 = performance.now()
const fetchOnceStatusWithPreapproval = await sdk.amulet.preapproval.fetchQuick(
    bob.partyId
)
const end2 = performance.now()

const duration2 = end2 - start2
if (duration < 1000) {
    logger.info(
        `Success! The operation was fast (${duration2.toFixed(2)} ms) and fetchOnce status is ${fetchOnceStatusWithPreapproval}.`
    )
} else {
    logger.warn(
        `Warning: Operation took longer than 1 second (${duration2.toFixed(2)} s).`
    )
}

const newExpiresAt = new Date(fetchedPreapprovalStatus!.expiresAt)
newExpiresAt.setDate(newExpiresAt.getDate() + 2)

await sdk.amulet.preapproval.renew({
    parties: {
        receiver: bob.partyId,
    },
    expiresAt: newExpiresAt,
})

const fetchedStatusAfterRenew = await sdk.amulet.preapproval.fetchStatus(
    bob.partyId,
    {
        oldCid: fetchedPreapprovalStatus!.contractId,
    }
)

const before = fetchedPreapprovalStatus!.expiresAt
const after = fetchedStatusAfterRenew!.expiresAt

if (!(after.getTime() > before.getTime())) {
    throw new Error(
        `Expected expiresAt to increase after renewal. before=${fetchedPreapprovalStatus!.expiresAt.toISOString()} after=${fetchedStatusAfterRenew!.expiresAt.toISOString()}`
    )
}

logger.info(
    {
        before: before.toISOString(),
        after: after.toISOString(),
        extendedSeconds: Math.round(
            (after.getTime() - before.getTime()) / 1000
        ),
    },
    'TransferPreapproval expiry extended, managed to renew preapproval'
)

// --- TEST CANCEL COMMAND
logger.info('Testing out cancel command')

if (!fetchedStatusAfterRenew?.templateId) {
    throw new Error('No preapproval found - fetchedPreapprovalStatus is null')
}
const [cancelPreapprovalCommand, cancelDisclosedContracts] =
    await sdk.amulet.preapproval.command.cancel({
        parties: {
            receiver: bob.partyId,
        },
    })

if (!cancelPreapprovalCommand) {
    throw Error(
        'Cancel preapproval command is null even though one has been created before'
    )
}

await sdk.ledger
    .prepare({
        partyId: bob.partyId,
        commands: cancelPreapprovalCommand,
        disclosedContracts: cancelDisclosedContracts,
    })
    .sign(bobKeys.privateKey)
    .execute({
        partyId: bob.partyId,
    })

logger.info('Submitted cancel command; now polling')
const cancelled = await sdk.amulet.preapproval.fetchStatus(bob.partyId, {
    cancelled: true,
})

const preapprovalACS = await sdk.ledger.acsReader.readJsContracts({
    parties: [bob.partyId],
    filterByParty: true,
})

const renewedPreapprovalStillActive = preapprovalACS.some(
    (contract) => contract.contractId === fetchedStatusAfterRenew?.contractId
)

if (cancelled === null && !renewedPreapprovalStillActive) {
    logger.info(`Successfully cancelled`)
}
```

**Buy Member Traffic**

<div className="before-after">
  ```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}}
  const buyMemberTrafficCommand =
     await sdk.tokenStandard.buyMemberTraffic(
        senderPartyId,
        amount,
        participantId,
        inputUtxosOptional
     )
  ```

  ***

  ```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}}
  const [buyTrafficCommand, buyTrafficDisclosedContracts] =
     await sdk.amulet.traffic.buy({
        buyer,
        ccAmount,
        inputUtxos: [],
     })
  ```
</div>

**Check Traffic Status**

<div className="before-after">
  ```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}}
  await sdk.tokenStandard.getMemberTrafficStatus(participantId)
  ```

  ***

  ```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}}
  await sdk.amulet.traffic.status()
  ```
</div>

Refer to the following example for more information:

```javascript title="Full example: buy member traffic" expandable theme={"theme":{"light":"github-light","dark":"github-dark"}}
import pino from 'pino'
import { localNetStaticConfig, SDK } from '@canton-network/wallet-sdk'
import {
    TOKEN_NAMESPACE_CONFIG,
    TOKEN_PROVIDER_CONFIG_DEFAULT,
    AMULET_NAMESPACE_CONFIG,
} from './utils/index.js'

const logger = pino({ name: 'v1-06-merge-utxos', level: 'info' })

const sdk = await SDK.create({
    auth: TOKEN_PROVIDER_CONFIG_DEFAULT,
    ledgerClientUrl: localNetStaticConfig.LOCALNET_APP_USER_LEDGER_URL,
    token: TOKEN_NAMESPACE_CONFIG,
    amulet: AMULET_NAMESPACE_CONFIG,
})
const aliceKeys = sdk.keys.generate()

const alice = await sdk.party.external
    .create(aliceKeys.publicKey, {
        partyHint: 'v1-07-alice',
    })
    .sign(aliceKeys.privateKey)
    .execute()

const bobKeys = sdk.keys.generate()

const bob = await sdk.party.external
    .create(bobKeys.publicKey, {
        partyHint: 'v1-07-bob',
    })
    .sign(bobKeys.privateKey)
    .execute()

const createPreapprovalCommand = await sdk.amulet.preapproval.command.create({
    parties: {
        receiver: bob.partyId,
    },
})

await sdk.ledger
    .prepare({
        partyId: bob.partyId,
        commands: createPreapprovalCommand,
    })
    .sign(bobKeys.privateKey)
    .execute({
        partyId: bob.partyId,
    })

// Mint holdings for alice

const [amuletTapCommand, amuletTapDisclosedContracts] = await sdk.amulet.tap(
    alice.partyId,
    '2000000'
)

await sdk.ledger
    .prepare({
        partyId: alice.partyId,
        commands: amuletTapCommand,
        disclosedContracts: amuletTapDisclosedContracts,
    })
    .sign(aliceKeys.privateKey)
    .execute({ partyId: alice.partyId })

logger.info(`Tapped holdings for alice`)

const trafficStatusBeforePurchase = await sdk.amulet.traffic.status()

logger.info(
    `Traffic status before purchase: ${JSON.stringify(trafficStatusBeforePurchase)}`
)

const ccAmount = 200000

const [buyTrafficCommand, buyTrafficDisclosedContracts] =
    await sdk.amulet.traffic.buy({
        buyer: alice.partyId,
        ccAmount,
        inputUtxos: [],
    })

await sdk.ledger
    .prepare({
        partyId: alice.partyId,
        commands: buyTrafficCommand,
        disclosedContracts: buyTrafficDisclosedContracts,
    })
    .sign(aliceKeys.privateKey)
    .execute({ partyId: alice.partyId })

logger.info(`buy member traffic for sender (${alice.partyId}) party completed`)

const utxos = await sdk.token.utxos.list({ partyId: alice.partyId })
logger.info(utxos, 'alice utxos')

const sentValue = 2000

const [transferCommand, transferDisclosedContracts] =
    await sdk.token.transfer.create({
        sender: alice.partyId,
        recipient: bob.partyId,
        amount: sentValue.toString(),
        instrumentId: 'Amulet',
        registryUrl: localNetStaticConfig.LOCALNET_REGISTRY_API_URL,
    })

await sdk.ledger
    .prepare({
        partyId: alice.partyId,
        commands: transferCommand,
        disclosedContracts: transferDisclosedContracts,
    })
    .sign(aliceKeys.privateKey)
    .execute({ partyId: alice.partyId })

//TODO: This does not work when we run multiple code snippets parallel

// await new Promise((resolve) => setTimeout(resolve, 61_000))

// const trafficStatusAfterPurchaseAndSomeTime = await amulet.traffic.status()

// const difference =
//     trafficStatusAfterPurchaseAndSomeTime.traffic_status.target
//         .total_purchased -
//     trafficStatusBeforePurchase.traffic_status.target.total_purchased

// if (difference === ccAmount) {
//     logger.info(
//         {
//             trafficStatusBeforePurchase,
//             trafficStatusAfterPurchaseAndSomeTime,
//         },
//         'MemberTraffic status. Traffic purchased successfully'
//     )
// } else {
//     logger.error(
//         {
//             trafficStatusBeforePurchase,
//             trafficStatusAfterPurchaseAndSomeTime,
//         },
//         'MemberTraffic status.'
//     )
//     throw new Error(
//         `Member traffic difference is ${difference}, expected ${ccAmount} `
//     )
// }
```

**Tap**

The is useful for testing against LocalNet or Devnet.

<div className="before-after">
  ```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}}
  await sdk.tokenStandard.createTap(partyId,
         amount,
         {
         instrumentId,
         instrumentAdmin
         })
  ```

  ***

  ```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}}
  await sdk.amulet.tap(partyId, amount)
  ```
</div>

## Migration reference

| v0 method                                           | v1 method                               |
| --------------------------------------------------- | --------------------------------------- |
| `sdk.tokenStandard.getMemberTrafficStatus`          | `sdk.amulet.traffic.status`             |
| `sdk.tokenStandard.buyMemberTraffic`                | `sdk.amulet.traffic.buy`                |
| `sdk.userLedger.createTransferPreapprovalCommand`   | `sdk.amulet.preapproval.command.create` |
| `sdk.tokenStandard.getTransferPreApprovalByParty`   | `sdk.amulet.preapproval.fetchStatus`    |
| `sdk.tokenStandard.createRenewTransferPreapproval`  | `sdk.amulet.preapproval.renew`          |
| `sdk.tokenStandard.createCancelTransferPreapproval` | `sdk.amulet.preapproval.command.cancel` |
| `sdk.tokenStandard.createTap`                       | `sdk.amulet.tap`                        |
| `sdk.tokenStandard.lookupFeaturedApps`              | `sdk.amulet.featuredApp.rights`         |
| `sdk.tokenStandard.selfGrantFeatureAppRights`       | `sdk.amulet.featuredApp.grant`          |

## See also

* [v0 to v1 migration overview](/sdks-tools/sdks/wallet-sdk/using-the-sdk/v0-to-v1-migration)
* [Migration cheat sheet](/sdks-tools/sdks/wallet-sdk/using-the-sdk/v0-to-v1-migration/migration-cheat-sheet)
* [Configuration](/sdks-tools/sdks/wallet-sdk/using-the-sdk/configuration) — SDK configuration
* [Performing a CC tap](/sdks-tools/sdks/wallet-sdk/guides/performing-a-cc-tap) — Canton Coin tap guide
* [Token namespace](/sdks-tools/sdks/wallet-sdk/using-the-sdk/v0-to-v1-migration/token) — Token transfers and UTXOs
