{"_id":"@cancore/dapp-connector","_rev":"2-9bca024c1eaeb1723217087ba6624ff2","name":"@cancore/dapp-connector","dist-tags":{"latest":"0.1.1"},"versions":{"0.1.0":{"name":"@cancore/dapp-connector","version":"0.1.0","keywords":["canton","cip-0103","wallet","dapp","cancore","provider","partylayer"],"license":"Apache-2.0","_id":"@cancore/dapp-connector@0.1.0","maintainers":[{"name":"merqry","email":"merqry.dev@gmail.com"}],"homepage":"https://docs.cancore.io/sdk/dapp-connector","bugs":{"url":"https://github.com/Cancore-io/sdk/issues"},"dist":{"shasum":"9feaa285aca7b85e3e786304a2a2610c98c96d71","tarball":"https://registry.npmjs.org/@cancore/dapp-connector/-/dapp-connector-0.1.0.tgz","fileCount":7,"integrity":"sha512-AQSnwEEexGt7q1LLPXCTU5YGyUW4cVYFf9AUiKkNH0bTBu0FFXfZUPvutgq+G6HAuY4lx7udECpBTJVCBTNtPw==","signatures":[{"sig":"MEQCIFz1R9J91PD7SzfMbl6lg/bK6uEHw4BA01U1LZ3tqnZCAiASvqtTZCQP1t2bbCijHhBJxbTNiChwO1XtHfaiTCWLqw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":69795},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js"}},"gitHead":"d2dadad21aaa752d16610f06c90b66d676a12ac1","scripts":{"build":"tsup","prepublishOnly":"npm run build"},"_npmUser":{"name":"merqry","email":"merqry.dev@gmail.com"},"repository":{"url":"git+https://github.com/Cancore-io/sdk.git","type":"git","directory":"packages/dapp-connector"},"_npmVersion":"11.6.0","description":"What a third-party dApp loads to reach a Cancore wallet: a CIP-0103 remote-profile provider over our JSON-RPC + SSE surface, and the PartyLayer discovery-adapter around it. No dependencies: the transport is fetch, EventSource and postMessage.","directories":{},"sideEffects":false,"_nodeVersion":"24.9.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/dapp-connector_0.1.0_1788539097859_0.5972087195457931","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"_id":"@cancore/dapp-connector@0.1.1","bugs":{"url":"https://github.com/Cancore-io/sdk/issues"},"dist":{"shasum":"a44126ff37d56169a1642588d77314117ea0f579","tarball":"https://registry.npmjs.org/@cancore/dapp-connector/-/dapp-connector-0.1.1.tgz","fileCount":7,"integrity":"sha512-Vun1Z4n2vQBL704mzFC88ZNiRFWqEWZ0tHEEQJCguQu//HTa8La8X9a59GPn/g9yVWd0rTuPCYu9a1COeYJ3xw==","signatures":[{"sig":"MEUCIEkfcYAfvS98Q2Rp12OaXKUdOxzl1gRgY0mFEgfzks9TAiEAyGkBsCKQHQV73ME9SpBP6ihAAZ+9hhJXOhzEtzMFptI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIGLaAYkTx6ydGtMjjSQoYAunZhYrbiUUR51ltzxW7gQTAiBezkVkb7Uus4qX670s0WTKkmETYVrcBeE5Jn+74ZhvWg=="}],"unpackedSize":74607},"main":"./dist/index.js","name":"@cancore/dapp-connector","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js"}},"gitHead":"8981a41eeb48d4b948f1a724725d7ef26c2b5122","license":"Apache-2.0","scripts":{"build":"tsup","prepublishOnly":"npm run build"},"version":"0.1.1","_npmUser":{"name":"merqry","email":"merqry.dev@gmail.com"},"homepage":"https://docs.cancore.io/sdk/dapp-connector","keywords":["canton","cip-0103","wallet","dapp","cancore","provider","partylayer"],"repository":{"url":"git+https://github.com/Cancore-io/sdk.git","type":"git","directory":"packages/dapp-connector"},"_npmVersion":"11.6.0","description":"What a third-party dApp loads to reach a Cancore wallet: a CIP-0103 remote-profile provider over our JSON-RPC + SSE surface, and the PartyLayer discovery-adapter around it. No dependencies: the transport is fetch, EventSource and postMessage.","directories":{},"maintainers":[{"name":"merqry","email":"merqry.dev@gmail.com"}],"sideEffects":false,"_nodeVersion":"24.9.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/dapp-connector_0.1.1_1788789291795_0.5516579300470361"}}},"time":{"created":"2026-09-04T16:24:57.567Z","modified":"2026-09-07T13:54:52.082Z","0.1.0":"2026-09-04T16:24:57.998Z","0.1.1":"2026-09-07T13:54:51.886Z"},"bugs":{"url":"https://github.com/Cancore-io/sdk/issues"},"license":"Apache-2.0","homepage":"https://docs.cancore.io/sdk/dapp-connector","keywords":["canton","cip-0103","wallet","dapp","cancore","provider","partylayer"],"repository":{"url":"git+https://github.com/Cancore-io/sdk.git","type":"git","directory":"packages/dapp-connector"},"description":"What a third-party dApp loads to reach a Cancore wallet: a CIP-0103 remote-profile provider over our JSON-RPC + SSE surface, and the PartyLayer discovery-adapter around it. No dependencies: the transport is fetch, EventSource and postMessage.","maintainers":[{"name":"merqry","email":"merqry.dev@gmail.com"}],"readme":"# `@cancore/dapp-connector`\n\nWhat a third-party dApp loads to reach a **Cancore wallet**: a\n[CIP-0103](https://github.com/global-synchronizer-foundation/cips) provider (remote profile)\nover Cancore's JSON-RPC + SSE surface, the consent ceremony that obtains a grant, and a\nPartyLayer discovery adapter around both.\n\n```bash\nnpm install @cancore/dapp-connector\n```\n\n**No dependencies.** The transport is `fetch`, `EventSource` and `postMessage`.\n**No keys, ever** — a request becomes a row the wallet owner answers on their own device, and\nwhat comes back is the outcome, never the material.\n\n## Quick start\n\n```ts\nimport { CancoreProvider } from '@cancore/dapp-connector';\n\nconst provider = new CancoreProvider({\n  host: 'https://app.cancore.io',      // the wallet's origin\n  appName: 'My dApp',                  // shown on the consent screen\n  scopes: ['wallet:accounts', 'wallet:sign'],\n});\n\nawait provider.request({ method: 'connect' });          // opens the consent popup\nconst accounts = await provider.request({ method: 'listAccounts' });\n\n// Asking for a signature returns a place the person goes — never a signature.\nconst { messageId, userUrl } = await provider.request({\n  method: 'signMessage',\n  params: { message: 'Sign in to My dApp\\nNonce: 7f3a…' },\n});\n\nprovider.on('messageSignature', ({ messageId, signature }) => { /* … */ });\n```\n\n## Options\n\n| Option | Meaning |\n| --- | --- |\n| `host` | wallet origin, e.g. `https://app.cancore.io` — the only required option |\n| `appName` | name shown on the consent screen; display only, the wallet trusts your origin, not this string |\n| `scopes` | defaults to the read-only pair; add `wallet:sign` to ask for signatures |\n| `session` | a grant you already hold — skips the ceremony entirely |\n| `win`, `fetchImpl`, `openStream`, `schedule` | seams for tests and non-browser hosts |\n\n`connect` must be called **inside the user's click**: the ceremony opens a popup, and a\nbrowser blocks a `window.open` that is not synchronous with the gesture. The provider is\nwritten so everything up to the open is synchronous.\n\n## The grant\n\n```ts\nconst session = provider.session;   // { token, rpcUrl, scopes, expiresAt } — or use runConnectCeremony\nlocalStorage.setItem('cancore-session', JSON.stringify(session));\n```\n\nPass it back as `session` next time and the popup never appears. The token is bound to your\norigin by the backend, and `rpcUrl` is told to you by the wallet rather than hardcoded — a\nstand can move its RPC endpoint without your build changing.\n\nThe owner can revoke a grant from their wallet at any moment; every later call then fails with\n`4900`.\n\n`runConnectCeremony({ host, appName, scopes, win })` runs only the ceremony, if you want to\nobtain a grant without constructing a provider.\n\n## Methods\n\nEach is behind the scope it needs — `wallet:connect`, `wallet:accounts`, `wallet:sign`.\n\n| Method | Scope | Returns |\n| --- | --- | --- |\n| `status` | — | wallet status |\n| `connect` | `wallet:connect` | the grant; opens the consent popup unless one was supplied |\n| `isConnected` | — | whether the grant is still usable |\n| `disconnect` | — | drops the grant |\n| `getActiveNetwork` | — | the network the wallet is on |\n| `listAccounts` | `wallet:accounts` | the owner's accounts |\n| `getPrimaryAccount` | `wallet:accounts` | the account the owner treats as primary |\n| `signMessage` | `wallet:sign` | `{ messageId, userUrl }` — a place the person goes |\n| `prepareExecute` | `wallet:sign` | `{ commandId, userUrl }` — same shape, for a transaction |\n| `cancore_getMessageSignature` | `wallet:sign` | the signature for a `messageId`, once it exists |\n| `cancore_getTxOutcome` | `wallet:sign` | the outcome for a `commandId`, once it exists |\n| `cancore_streamTicket` | — | one-shot ticket the event stream authenticates with |\n\n### Asking for a signature\n\n`signMessage` and `prepareExecute` do not return a signature. They return an id and a\n`userUrl`, because in the remote profile the person is somewhere else — another tab, another\ndevice — and a signature exists only after they have looked at what they are signing. Show the\nURL, then wait for the event or poll the outcome lookup.\n\nTwo methods are **answered `4200 Unsupported Method` by the wallet, on purpose**:\n`ledgerApi`, because the CIP forbids a server-side provider from proxying ledger reads (and\nhanding out an access token is worse), and `prepareExecuteAndWait`, because it may only answer\nonce the transaction completes — which here waits on a human, so the wait could only ever time\nout. Use `prepareExecute` plus `txChanged`.\n\nThey are listed as known methods so that the refusal is what you receive. `-32601` from this\nlibrary would say the method does not exist, which is a different sentence: the name is right,\nthe provider is not obliged.\n\n## Events\n\n```ts\nprovider.on('txChanged', (payload) => { /* … */ });\n```\n\n| Event | Fires when |\n| --- | --- |\n| `connected` | the stream is live |\n| `statusChanged` | the wallet's status changed |\n| `accountsChanged` | the owner's account list changed |\n| `txChanged` | a transaction you prepared moved — including to its final state |\n| `messageSignature` | a message you asked about was signed |\n\nThe stream is SSE, authenticated by a one-shot ticket, and reopened with a fresh ticket when\nthe connection drops (3 s between attempts — the stream has no replay buffer, and a reconnect\nstorm would only make that worse).\n\n**Because there is no replay buffer**, a dropped mobile connection must not leave a dApp\nunable to learn whether the user's money moved. That is what `cancore_getTxOutcome` and\n`cancore_getMessageSignature` are for: ask again, at any time, and get the same answer.\n\n## Errors\n\nEvery rejection carries a numeric `code` at the **top level**, as EIP-1193 and CIP-0103\npromise. This is the reason the package exists rather than reusing the upstream async\nprovider, whose transport throws the whole JSON-RPC envelope instead — code first, so\n`err.code === 4001` works.\n\n| Code | Constant | Means |\n| --- | --- | --- |\n| `4001` | `USER_REJECTED` | the person said no |\n| `4100` | `UNAUTHORIZED` | the grant does not carry the scope this method needs |\n| `4200` | `UNSUPPORTED_METHOD` | a real method this provider is not obliged to serve |\n| `4900` | `DISCONNECTED` | no usable grant — revoked, expired, or never obtained |\n| `4901` | `CHAIN_DISCONNECTED` | the wallet is not connected to its network |\n| `-32700 … -32603` | `PARSE_ERROR`, `INVALID_REQUEST`, `METHOD_NOT_FOUND`, `INVALID_PARAMS`, `INTERNAL` | JSON-RPC |\n| `-32000 … -32005` | `INVALID_INPUT`, `RESOURCE_NOT_FOUND`, `RESOURCE_UNAVAILABLE`, `TRANSACTION_REJECTED`, `METHOD_NOT_SUPPORTED`, `LIMIT_EXCEEDED` | CIP-0103 application range |\n\n`RpcCode` exports all sixteen; `rpcError(code, message, data?)` builds one in the same shape,\nfor tests and for wrapping.\n\n## PartyLayer adapter\n\n```ts\nimport { cancoreAdapterFactory } from '@cancore/dapp-connector';\n\nconst adapter = cancoreAdapterFactory({ appName: 'My dApp' }).create('https://app.cancore.io');\nif (await adapter.detect()) {\n  const provider = adapter.provider();\n}\n```\n\n`getInfo()` returns the registry entry's shape (id, name, `type: 'remote'`, description, icon,\nurl), `detect()` asks whether that host really is a Cancore wallet, and `teardown()` closes\nthe stream. Nothing about a Cancore URL is hardcoded in your app.\n\n## What it deliberately cannot do\n\nRead a balance or query the ledger; obtain a key, a seed or an access token; or reach any\nCancore route the grant's scopes do not name. A prompt-injected dApp gets the same answer a\ncareless one does.\n\n## Full documentation\n\n**<https://docs.cancore.io/sdk/dapp-connector>** — the consent ceremony and the three rules it\nowes, every method with its scope, the event stream, the error codes, and the PartyLayer\nadapter.\n\n## License\n\nApache-2.0.\n","readmeFilename":"README.md"}