diff --git a/.agents/skills/oneshot-embedded-wallet/SKILL.md b/.agents/skills/oneshot-embedded-wallet/SKILL.md index 35aac78..aae79a9 100644 --- a/.agents/skills/oneshot-embedded-wallet/SKILL.md +++ b/.agents/skills/oneshot-embedded-wallet/SKILL.md @@ -111,12 +111,26 @@ await proxy.rpc("configure", options); | `copy.personalSign.rejectLabel` | string | Reject button | | `copy.personalSign.signLabel` | string | Sign button | | `copy.typedData.title` | string | EIP-712 modal title | -| `copy.typedData.accountLabel` | string | Account field label | +| `copy.typedData.body` | string | Intro paragraph | +| `copy.typedData.networkLabel` | string | Network summary row label | +| `copy.typedData.requestFromLabel` | string | Requesting domain row label | +| `copy.typedData.accountLabel` | string | Signing account row label | +| `copy.typedData.interactingWithLabel` | string | Verifying contract row label | | `copy.typedData.primaryTypeLabel` | string | Primary type label | -| `copy.typedData.domainLabel` | string | Domain label | -| `copy.typedData.messageLabel` | string | Message label | +| `copy.typedData.messageSectionLabel` | string | Message card heading | +| `copy.typedData.signingHint` | string | Hint shown while signing | | `copy.typedData.rejectLabel` | string | Reject button | | `copy.typedData.signLabel` | string | Sign button | +| `copy.grantExecutionPermission.title` | string | ERC-20 periodic grant modal title | +| `copy.grantExecutionPermission.permissionKindLabel` | string | Summary card eyebrow | +| `copy.grantExecutionPermission.amountLabel` | string | Summary amount row | +| `copy.grantExecutionPermission.transferWindowLabel` | string | Summary cadence row | +| `copy.grantExecutionPermission.toLabel` | string | Delegate summary row | +| `copy.grantExecutionPermission.viewOnExplorerLabel` | string | Delegate explorer link a11y | +| `copy.grantExecutionPermission.justificationLabel` | string | Host justification in terms card | +| `copy.grantExecutionPermission.advancedLabel` | string | Toggle for Unix start field | +| `copy.grantExecutionPermission.grantLabel` | string | Grant button | +| `copy.grantExecutionPermission.rejectLabel` | string | Reject button | | `copy.credentialOffer.title` | string | offer modal title | | `copy.credentialOffer.body` | string | supports `{issuerName}` `{issuerId}` | | `copy.credentialOffer.offeredHeading` | string | offered list heading | @@ -389,8 +403,8 @@ EIP-7715 host RPCs: `wallet_requestExecutionPermissions`, `wallet_revokeExecutio | `permission.type` | Chains (v1) | Purpose | |-------------------|-------------|---------| | `erc20-token-periodic` | All relayer chains | Periodic ERC-20 `transfer` budget (`ScopeType.Erc20PeriodTransfer`) | -| `lifi-swap-approve` | Base (`0x2105`) | One-time `approve(inputToken → LiFi Diamond)` onboarding | -| `lifi-swap-periodic` | Base (`0x2105`) | Periodic LiFi swap via `LiFiSwapEnforcer` (`0x47472E8AA7012D1c23336aa28514AE94389318f5`) | +| `lifi-swap-approve` | Arc (`0x13b2`), Base (`0x2105`), Ethereum (`0x1`) | One-time `approve(inputToken → LiFi Diamond)` onboarding | +| `lifi-swap-periodic` | Arc, Base, Ethereum mainnet | Periodic LiFi swap via `LiFiSwapEnforcer` proxy (`0x29fcBBa852439616c4D614A2fa6411E42b760153`) | **`erc20-token-periodic` / `lifi-swap-periodic` amounts:** Hosts should pass `periodAmount` (hex atoms) to prefill the grant form. Playground demos default to **10 USDC** (`0x989680`). diff --git a/.github/workflows/Deploy Dev.yaml b/.github/workflows/Deploy Dev.yaml index 0da3bff..c227225 100644 --- a/.github/workflows/Deploy Dev.yaml +++ b/.github/workflows/Deploy Dev.yaml @@ -55,11 +55,8 @@ jobs: gcloud --quiet auth configure-docker $GAR_LOCATION-docker.pkg.dev - name: Build Wallet - env: - CLOUDSMITH_TOKEN: ${{ secrets.CLOUDSMITH_TOKEN }} run: |- docker build \ - --secret id=cloudsmith_token,env=CLOUDSMITH_TOKEN \ --tag "$GAR_LOCATION-docker.pkg.dev/$PROJECT_ID/$PRODUCT_NAME/oneshot-wallet:$GITHUB_SHA" \ --tag "$GAR_LOCATION-docker.pkg.dev/$PROJECT_ID/$PRODUCT_NAME/oneshot-wallet:${GITHUB_REF##*/}" \ --tag "$GAR_LOCATION-docker.pkg.dev/$PROJECT_ID/$PRODUCT_NAME/oneshot-wallet:latest" \ diff --git a/.github/workflows/Deploy Prod.yaml b/.github/workflows/Deploy Prod.yaml index 0811ddd..0ba27c3 100644 --- a/.github/workflows/Deploy Prod.yaml +++ b/.github/workflows/Deploy Prod.yaml @@ -53,11 +53,8 @@ jobs: gcloud --quiet auth configure-docker $GAR_LOCATION-docker.pkg.dev - name: Build Wallet - env: - CLOUDSMITH_TOKEN: ${{ secrets.CLOUDSMITH_TOKEN }} run: |- docker build \ - --secret id=cloudsmith_token,env=CLOUDSMITH_TOKEN \ --tag "$GAR_LOCATION-docker.pkg.dev/$PROJECT_ID/$PRODUCT_NAME/oneshot-wallet:$GITHUB_SHA" \ --tag "$GAR_LOCATION-docker.pkg.dev/$PROJECT_ID/$PRODUCT_NAME/oneshot-wallet:${GITHUB_REF##*/}" \ --tag "$GAR_LOCATION-docker.pkg.dev/$PROJECT_ID/$PRODUCT_NAME/oneshot-wallet:latest" \ diff --git a/.npmrc b/.npmrc deleted file mode 100644 index e87ec79..0000000 --- a/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@crcl-main:registry=https://npm.cloudsmith.io/circle/common-private/ -//npm.cloudsmith.io/circle/common-private/:_authToken=${CLOUDSMITH_TOKEN} diff --git a/AGENTS.md b/AGENTS.md index 4806eae..a3c0686 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -25,7 +25,7 @@ Prefer **clean code over backwards compatibility**. Do not add legacy redirects, Test Host Layer: `host/` (`npm run dev:host`). Browser extension: `extension/` (`npm run dev:extension`) — see [`extension/README.md`](extension/README.md). Branding / host config via Host RPC `configure`, not in-wallet debug knobs. -Fiat onramp: Asset Details **Buy** and host RPC `onramp({ chainId?, amount? })` open `OnrampView` (Circle AppKit). Sessions come from Relayer `POST /wallet/onramp` — never put the Circle kit key in this SPA. When Branding is nested in a Host iframe (`window.self !== window.top`), UI prefers `openWindow` (prefetch session, then a sync click) because Transak `frame-ancestors` cannot authorize arbitrary hosts. Top-level Branding uses `mountIframe`. Override with `localStorage.circlePopup = "true"|"false"`. Cloudsmith: set `CLOUDSMITH_TOKEN` before `npm install` (see README). +Fiat onramp: Asset Details **Buy** and host RPC `onramp({ chainId?, amount? })` open `OnrampView` (Circle AppKit). Sessions come from Relayer `POST /wallet/onramp` — never put the Circle kit key in this SPA. When Branding is nested in a Host iframe (`window.self !== window.top`), UI prefers `openWindow` (prefetch session, then a sync click) because Transak `frame-ancestors` cannot authorize arbitrary hosts. Top-level Branding uses `mountIframe`. Override with `localStorage.circlePopup = "true"|"false"`. Circle App Kit is the public npm package `@circle-fin/app-kit`. CCTP bridge: Asset Details **Bridge** (native USDC with `useCCTPBridge`) and host RPC `bridge({ amount?, sourceChainId?, destinationChainId? })` open `CCTPBridge`. Source omit → session chain. Burns via `TokenMessengerV2.depositForBurnWithHook` + `cctp-forward` hook through the EIP-7710 relayer (same USDC fee as Send). Destination mint is Circle’s Forwarding Service — no dest-chain signature, no native gas, no BridgeKit. diff --git a/Dockerfile b/Dockerfile index 78f54aa..c57ef0b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,13 +5,8 @@ WORKDIR /app COPY package.json package-lock.json ./ COPY host/package.json ./host/ -COPY .npmrc ./ -# Circle @crcl-main/* requires CLOUDSMITH_TOKEN BuildKit secret -RUN --mount=type=secret,id=cloudsmith_token \ - CLOUDSMITH_TOKEN="$(cat /run/secrets/cloudsmith_token)" \ - npm ci \ - && rm -f .npmrc +RUN npm ci COPY index.html vite.config.ts tsconfig.json tsconfig.node.json components.json ./ COPY src ./src diff --git a/README.md b/README.md index 05c049b..aa0dc29 100644 --- a/README.md +++ b/README.md @@ -29,21 +29,6 @@ Production deliverable: a static **nginx** Docker image (no server-side runtime) ## Setup -Circle’s private AppKit canary requires Cloudsmith auth. The repo includes `.npmrc`; -export your token before installing: - -```bash -# Linux / macOS -export CLOUDSMITH_TOKEN= -``` - -```powershell -# Windows PowerShell -$env:CLOUDSMITH_TOKEN = "" -``` - -CI Docker builds expect GitHub Actions secret `CLOUDSMITH_TOKEN`. - ```bash npm install cp .env.example .env # set NGROK_AUTHTOKEN (and optional NGROK_DOMAIN) diff --git a/host/src/components/WalletConfiguratorTextTabSigningSections.tsx b/host/src/components/WalletConfiguratorTextTabSigningSections.tsx index ea03d96..a76ef81 100644 --- a/host/src/components/WalletConfiguratorTextTabSigningSections.tsx +++ b/host/src/components/WalletConfiguratorTextTabSigningSections.tsx @@ -219,6 +219,12 @@ export function WalletConfiguratorTextTabSigningSections({ value={form.typedTitle} onChange={(value) => patch("typedTitle", value)} /> + patch("typedBody", value)} + /> patch("typedReject", value)} /> + patch("typedSigningHint", value)} + /> @@ -243,36 +255,18 @@ export function WalletConfiguratorTextTabSigningSections({ value={form.siweTitle} onChange={(value) => patch("siweTitle", value)} /> - patch("siweBody", value)} /> - patch("siweEstimatedChangesLabel", value)} - /> - patch("siweNoChangesLabel", value)} - /> patch("siweNetworkLabel", value)} /> - patch("siweRequestFromLabel", value)} - /> patch("siweUriLabel", value)} /> + patch("siweVersionLabel", value)} + /> + patch("siweNonceLabel", value)} + /> + patch("siweIssuedAtLabel", value)} + /> + patch("siweExpirationTimeLabel", value)} + /> + patch("siweNotBeforeLabel", value)} + /> + patch("siweResourcesLabel", value)} + /> patch("siweSignLabel", value)} /> diff --git a/host/src/components/WalletConfiguratorTextTabWalletSections.tsx b/host/src/components/WalletConfiguratorTextTabWalletSections.tsx index 26f0a2f..9374783 100644 --- a/host/src/components/WalletConfiguratorTextTabWalletSections.tsx +++ b/host/src/components/WalletConfiguratorTextTabWalletSections.tsx @@ -171,6 +171,54 @@ export function WalletConfiguratorTextTabWalletSections({ value={form.confirmTransferReject} onChange={(value) => patch("confirmTransferReject", value)} /> + patch("onrampTitle", value)} + /> + patch("onrampBody", value)} + /> + patch("onrampDestinationLabel", value)} + /> + patch("onrampCloseLabel", value)} + /> + patch("onrampOpenLabel", value)} + /> + patch("onrampReopenLabel", value)} + /> + patch("onrampPopupReadyBody", value)} + /> + patch("onrampPopupOpenedBody", value)} + /> patch("grantPermissionTitle", value)} /> + patch("grantPermissionKindLabel", value)} + /> + patch("grantPermissionAmountLabel", value)} + /> + + patch("grantPermissionTransferWindowLabel", value) + } + /> const typedData: Record = {}; put(typedData, "title", form.typedTitle); + put(typedData, "body", form.typedBody); + put(typedData, "networkLabel", form.typedNetworkLabel); + put(typedData, "requestFromLabel", form.typedRequestFromLabel); + put(typedData, "accountLabel", form.typedAccountLabel); + put(typedData, "interactingWithLabel", form.typedInteractingWithLabel); + put(typedData, "primaryTypeLabel", form.typedPrimaryTypeLabel); + put(typedData, "messageSectionLabel", form.typedMessageSectionLabel); put(typedData, "signLabel", form.typedSignLabel); put(typedData, "rejectLabel", form.typedReject); + put(typedData, "signingHint", form.typedSigningHint); if (Object.keys(typedData).length > 0) copy.typedData = typedData; const siwe: Record = {}; put(siwe, "title", form.siweTitle); put(siwe, "body", form.siweBody); - put(siwe, "estimatedChangesLabel", form.siweEstimatedChangesLabel); - put(siwe, "noChangesLabel", form.siweNoChangesLabel); put(siwe, "networkLabel", form.siweNetworkLabel); - put(siwe, "requestFromLabel", form.siweRequestFromLabel); put(siwe, "signingInWithLabel", form.siweSigningInWithLabel); put(siwe, "messageLabel", form.siweMessageLabel); put(siwe, "uriLabel", form.siweUriLabel); + put(siwe, "versionLabel", form.siweVersionLabel); + put(siwe, "nonceLabel", form.siweNonceLabel); + put(siwe, "issuedAtLabel", form.siweIssuedAtLabel); + put(siwe, "expirationTimeLabel", form.siweExpirationTimeLabel); + put(siwe, "notBeforeLabel", form.siweNotBeforeLabel); + put(siwe, "resourcesLabel", form.siweResourcesLabel); put(siwe, "rejectLabel", form.siweRejectLabel); put(siwe, "signLabel", form.siweSignLabel); put(siwe, "signingHint", form.siweSigningHint); @@ -794,6 +885,21 @@ function buildNestedCopyFromForm(form: IStyleFormState): Record copy.confirmTransfer = confirmTransfer; } + const onramp: Record = {}; + put(onramp, "title", form.onrampTitle); + put(onramp, "body", form.onrampBody); + put(onramp, "destinationLabel", form.onrampDestinationLabel); + put(onramp, "closeLabel", form.onrampCloseLabel); + put(onramp, "openLabel", form.onrampOpenLabel); + put(onramp, "reopenLabel", form.onrampReopenLabel); + put(onramp, "loadingLabel", form.onrampLoadingLabel); + put(onramp, "preparingLabel", form.onrampPreparingLabel); + put(onramp, "popupReadyBody", form.onrampPopupReadyBody); + put(onramp, "popupOpenedBody", form.onrampPopupOpenedBody); + if (Object.keys(onramp).length > 0) { + copy.onramp = onramp; + } + const transferTokens: Record = {}; put(transferTokens, "title", form.transferTokensTitle); put(transferTokens, "sendLabel", form.transferTokensSend); @@ -817,6 +923,32 @@ function buildNestedCopyFromForm(form: IStyleFormState): Record const grantExecutionPermission: Record = {}; put(grantExecutionPermission, "title", form.grantPermissionTitle); + put( + grantExecutionPermission, + "permissionKindLabel", + form.grantPermissionKindLabel, + ); + put(grantExecutionPermission, "amountLabel", form.grantPermissionAmountLabel); + put( + grantExecutionPermission, + "transferWindowLabel", + form.grantPermissionTransferWindowLabel, + ); + put( + grantExecutionPermission, + "justificationLabel", + form.grantPermissionJustificationLabel, + ); + put( + grantExecutionPermission, + "viewOnExplorerLabel", + form.grantPermissionViewOnExplorerLabel, + ); + put( + grantExecutionPermission, + "advancedLabel", + form.grantPermissionAdvancedLabel, + ); put(grantExecutionPermission, "grantLabel", form.grantPermissionGrant); put(grantExecutionPermission, "rejectLabel", form.grantPermissionReject); if (Object.keys(grantExecutionPermission).length > 0) { diff --git a/package-lock.json b/package-lock.json index bb942f5..acbb8d7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "@1shotapi/ows-signer-utils": "^0.6.2", "@1shotapi/ows-types": "^0.8.0", "@1shotapi/ows-wallet-utils": "^0.5.1", - "@crcl-main/app-kit": "^1.14.0-canary-feature-onramp-kit-sdk.1789055534", + "@circle-fin/app-kit": "^1.15.2", "@fontsource-variable/geist": "^5.3.0", "@metamask/smart-accounts-kit": "^2.0.0", "@reown/walletkit": "^1.5.6", @@ -1025,103 +1025,18 @@ "win32" ] }, - "node_modules/@coral-xyz/anchor": { - "version": "0.31.1", - "resolved": "https://registry.npmjs.org/@coral-xyz/anchor/-/anchor-0.31.1.tgz", - "integrity": "sha512-QUqpoEK+gi2S6nlYc2atgT2r41TT3caWr/cPUEL8n8Md9437trZ68STknq897b82p5mW0XrTBNOzRbmIRJtfsA==", - "license": "(MIT OR Apache-2.0)", - "dependencies": { - "@coral-xyz/anchor-errors": "^0.31.1", - "@coral-xyz/borsh": "^0.31.1", - "@noble/hashes": "^1.3.1", - "@solana/web3.js": "^1.69.0", - "bn.js": "^5.1.2", - "bs58": "^4.0.1", - "buffer-layout": "^1.2.2", - "camelcase": "^6.3.0", - "cross-fetch": "^3.1.5", - "eventemitter3": "^4.0.7", - "pako": "^2.0.3", - "superstruct": "^0.15.4", - "toml": "^3.0.0" - }, - "engines": { - "node": ">=17" - } - }, - "node_modules/@coral-xyz/anchor-errors": { - "version": "0.31.1", - "resolved": "https://registry.npmjs.org/@coral-xyz/anchor-errors/-/anchor-errors-0.31.1.tgz", - "integrity": "sha512-NhNEku4F3zzUSBtrYz84FzYWm48+9OvmT1Hhnwr6GnPQry2dsEqH/ti/7ASjjpoFTWRnPXrjAIT1qM6Isop+LQ==", - "license": "Apache-2.0", - "engines": { - "node": ">=10" - } - }, - "node_modules/@coral-xyz/anchor/node_modules/base-x": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.11.tgz", - "integrity": "sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA==", - "license": "MIT", - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, - "node_modules/@coral-xyz/anchor/node_modules/bs58": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", - "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", - "license": "MIT", - "dependencies": { - "base-x": "^3.0.2" - } - }, - "node_modules/@coral-xyz/anchor/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@coral-xyz/anchor/node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "license": "MIT" - }, - "node_modules/@coral-xyz/borsh": { - "version": "0.31.1", - "resolved": "https://registry.npmjs.org/@coral-xyz/borsh/-/borsh-0.31.1.tgz", - "integrity": "sha512-9N8AU9F0ubriKfNE3g1WF0/4dtlGXoBN/hd1PvbNBamBNwRgHxH4P+o3Zt7rSEloW1HUs6LfZEchlx9fW7POYw==", - "license": "Apache-2.0", - "dependencies": { - "bn.js": "^5.1.2", - "buffer-layout": "^1.2.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@solana/web3.js": "^1.69.0" - } - }, - "node_modules/@crcl-main/app-kit": { - "version": "1.14.0-canary-feature-onramp-kit-sdk.1789055534", - "resolved": "https://npm.cloudsmith.io/circle/common-private/@crcl-main/app-kit/-/app-kit-1.14.0-canary-feature-onramp-kit-sdk.1789055534.tgz", - "integrity": "sha512-04GjiZqTZAiNz+KXvafEk/4TS3pXEWfZPk6HUoyKzIwpMsz8JD6ixNeBy4wuBLEcSCNSJd1A9IF25AWoHKODZQ==", - "dependencies": { + "node_modules/@circle-fin/app-kit": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@circle-fin/app-kit/-/app-kit-1.15.2.tgz", + "integrity": "sha512-7+MaJVcdUJ5Mr1Tonz5/b56gm1F1K2HML04iOimMUmKT3q3mfixNKztAZVH2bv+K6ZXMHXQ0SkI7ZYraOPsseg==", + "dependencies": { + "@circle-fin/bridge-kit": "1.15.1", + "@circle-fin/earn-kit": "1.7.0", + "@circle-fin/onramp-kit": "1.0.1", + "@circle-fin/provider-gateway-v1": "1.5.0", + "@circle-fin/swap-kit": "1.7.0", + "@circle-fin/unified-balance-kit": "1.7.0", "@coral-xyz/anchor": "^0.31.1", - "@crcl-main/bridge-kit": "1.14.1-canary-feature-onramp-kit-sdk.1789055534", - "@crcl-main/earn-kit": "1.6.1-canary-feature-onramp-kit-sdk.1789055534", - "@crcl-main/onramp-kit": "0.0.1-canary-feature-onramp-kit-sdk.1789055534", - "@crcl-main/provider-gateway-v1": "1.4.0-canary-feature-onramp-kit-sdk.1789055534", - "@crcl-main/swap-kit": "1.6.1-canary-feature-onramp-kit-sdk.1789055534", - "@crcl-main/unified-balance-kit": "1.6.0-canary-feature-onramp-kit-sdk.1789055534", "@ethersproject/abi": "^5.8.0", "@ethersproject/address": "^5.8.0", "@ethersproject/bytes": "^5.8.0", @@ -1140,7 +1055,7 @@ "node": ">=20.0.0" } }, - "node_modules/@crcl-main/app-kit/node_modules/@noble/curves": { + "node_modules/@circle-fin/app-kit/node_modules/@noble/curves": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", @@ -1152,7 +1067,7 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/@crcl-main/app-kit/node_modules/@noble/hashes": { + "node_modules/@circle-fin/app-kit/node_modules/@noble/hashes": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", @@ -1164,7 +1079,7 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/@crcl-main/app-kit/node_modules/pino": { + "node_modules/@circle-fin/app-kit/node_modules/pino": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/pino/-/pino-10.1.0.tgz", "integrity": "sha512-0zZC2ygfdqvqK8zJIr1e+wT1T/L+LF6qvqvbzEQ6tiMAoTqEVK9a1K3YRu8HEUvGEvNqZyPJTtb2sNIoTkB83w==", @@ -1186,7 +1101,7 @@ "pino": "bin.js" } }, - "node_modules/@crcl-main/app-kit/node_modules/zod": { + "node_modules/@circle-fin/app-kit/node_modules/zod": { "version": "3.25.67", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.67.tgz", "integrity": "sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==", @@ -1195,13 +1110,14 @@ "url": "https://github.com/sponsors/colinhacks" } }, - "node_modules/@crcl-main/bridge-kit": { - "version": "1.14.1-canary-feature-onramp-kit-sdk.1789055534", - "resolved": "https://npm.cloudsmith.io/circle/common-private/@crcl-main/bridge-kit/-/bridge-kit-1.14.1-canary-feature-onramp-kit-sdk.1789055534.tgz", - "integrity": "sha512-oBze4ODsutrppjkhL0jq1JtxdFFgo4qYDSK3yScrDiE3CcohyOom5S8MvN/QY72xUteQKzCiDgXWpQqsB7PaSQ==", + "node_modules/@circle-fin/bridge-kit": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/@circle-fin/bridge-kit/-/bridge-kit-1.15.1.tgz", + "integrity": "sha512-CnMoBmZJL4NXYDZ11uHxdQJ+9/h9JQSJPOXmFw0EJRn45m0NqWJFWJgyOH+1Kmt8csDOcBgNLWii4WSn3wS4qA==", "dependencies": { - "@crcl-main/provider-cctp-v2": "1.13.0-canary-feature-onramp-kit-sdk.1789055534", - "@crcl-main/provider-fee-v1": "0.3.0-canary-feature-onramp-kit-sdk.1789055534", + "@circle-fin/provider-cctp-v2": "^1.14.0", + "@circle-fin/provider-cctpx": "^1.0.0", + "@circle-fin/provider-fee-v1": "^0.3.1", "@ethersproject/abi": "^5.8.0", "@ethersproject/address": "^5.8.0", "@ethersproject/bytes": "^5.8.0", @@ -1216,7 +1132,7 @@ "node": ">=20.0.0" } }, - "node_modules/@crcl-main/bridge-kit/node_modules/pino": { + "node_modules/@circle-fin/bridge-kit/node_modules/pino": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/pino/-/pino-10.1.0.tgz", "integrity": "sha512-0zZC2ygfdqvqK8zJIr1e+wT1T/L+LF6qvqvbzEQ6tiMAoTqEVK9a1K3YRu8HEUvGEvNqZyPJTtb2sNIoTkB83w==", @@ -1238,7 +1154,7 @@ "pino": "bin.js" } }, - "node_modules/@crcl-main/bridge-kit/node_modules/zod": { + "node_modules/@circle-fin/bridge-kit/node_modules/zod": { "version": "3.25.67", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.67.tgz", "integrity": "sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==", @@ -1247,12 +1163,12 @@ "url": "https://github.com/sponsors/colinhacks" } }, - "node_modules/@crcl-main/earn-kit": { - "version": "1.6.1-canary-feature-onramp-kit-sdk.1789055534", - "resolved": "https://npm.cloudsmith.io/circle/common-private/@crcl-main/earn-kit/-/earn-kit-1.6.1-canary-feature-onramp-kit-sdk.1789055534.tgz", - "integrity": "sha512-5YwMUf2PK8VsFlOoQv38ykp8gDxWbVqQkHFCgb9OaqfHmmPn9f/xwQDdluvAkuA85pj2UI9iJKc00VZyT50a5g==", + "node_modules/@circle-fin/earn-kit": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@circle-fin/earn-kit/-/earn-kit-1.7.0.tgz", + "integrity": "sha512-Yb04OT4UEX6IJoCApkWuuB6VTL8+dJpyvKb0ZcAKokKKvvnr8SwuWUlx1yYqd2x6Y9GJR6GyfYz5PZx1zyZiQA==", "dependencies": { - "@crcl-main/provider-earn-service": "1.5.1-canary-feature-onramp-kit-sdk.1789055534", + "@circle-fin/provider-earn-service": "^1.6.0", "@ethersproject/abi": "^5.8.0", "@ethersproject/address": "^5.8.0", "@ethersproject/bytes": "^5.8.0", @@ -1268,7 +1184,7 @@ "node": ">=20.0.0" } }, - "node_modules/@crcl-main/earn-kit/node_modules/pino": { + "node_modules/@circle-fin/earn-kit/node_modules/pino": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/pino/-/pino-10.1.0.tgz", "integrity": "sha512-0zZC2ygfdqvqK8zJIr1e+wT1T/L+LF6qvqvbzEQ6tiMAoTqEVK9a1K3YRu8HEUvGEvNqZyPJTtb2sNIoTkB83w==", @@ -1290,7 +1206,7 @@ "pino": "bin.js" } }, - "node_modules/@crcl-main/earn-kit/node_modules/zod": { + "node_modules/@circle-fin/earn-kit/node_modules/zod": { "version": "3.25.67", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.67.tgz", "integrity": "sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==", @@ -1299,10 +1215,10 @@ "url": "https://github.com/sponsors/colinhacks" } }, - "node_modules/@crcl-main/onramp-kit": { - "version": "0.0.1-canary-feature-onramp-kit-sdk.1789055534", - "resolved": "https://npm.cloudsmith.io/circle/common-private/@crcl-main/onramp-kit/-/onramp-kit-0.0.1-canary-feature-onramp-kit-sdk.1789055534.tgz", - "integrity": "sha512-xEpJA3oRgNGOKAZaDcjobmEZnxfDunFjNeWH3gZzCJJrj4mboNHPJVOJMpHPLOErfSBuilZcU+sQuvrwAJ0OrA==", + "node_modules/@circle-fin/onramp-kit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@circle-fin/onramp-kit/-/onramp-kit-1.0.1.tgz", + "integrity": "sha512-N8Mok3ybsEFl2+ZoDmAjjAPpqasQQu6FHIqPMwIups0eqaOEvu7I+GCg3P+wxelqm9eQLSZORXt6tUcUH9Ojzg==", "dependencies": { "pino": "10.1.0", "zod": "3.25.67" @@ -1311,7 +1227,7 @@ "node": ">=20.0.0" } }, - "node_modules/@crcl-main/onramp-kit/node_modules/pino": { + "node_modules/@circle-fin/onramp-kit/node_modules/pino": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/pino/-/pino-10.1.0.tgz", "integrity": "sha512-0zZC2ygfdqvqK8zJIr1e+wT1T/L+LF6qvqvbzEQ6tiMAoTqEVK9a1K3YRu8HEUvGEvNqZyPJTtb2sNIoTkB83w==", @@ -1333,7 +1249,7 @@ "pino": "bin.js" } }, - "node_modules/@crcl-main/onramp-kit/node_modules/zod": { + "node_modules/@circle-fin/onramp-kit/node_modules/zod": { "version": "3.25.67", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.67.tgz", "integrity": "sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==", @@ -1342,10 +1258,10 @@ "url": "https://github.com/sponsors/colinhacks" } }, - "node_modules/@crcl-main/provider-cctp-v2": { - "version": "1.13.0-canary-feature-onramp-kit-sdk.1789055534", - "resolved": "https://npm.cloudsmith.io/circle/common-private/@crcl-main/provider-cctp-v2/-/provider-cctp-v2-1.13.0-canary-feature-onramp-kit-sdk.1789055534.tgz", - "integrity": "sha512-Q6HmGkmeGee4OqogbJ9cP/hgfIlj2NNIHh03F5i20Oq8FGag81jbFlaSGTNzMTFMyRSy//zyc8vSANkmeBY2tA==", + "node_modules/@circle-fin/provider-cctp-v2": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/@circle-fin/provider-cctp-v2/-/provider-cctp-v2-1.14.0.tgz", + "integrity": "sha512-AX4fQueRft5TfS6XnYjPkHa6El+Dyf7Q2SyifLC6sfOT1kh2FdMvPKc4cWMdn5cBVQczDXlgXiP+PlOSrpK2qw==", "dependencies": { "@coral-xyz/anchor": "^0.31.1", "@ethersproject/abi": "^5.8.0", @@ -1372,7 +1288,7 @@ } } }, - "node_modules/@crcl-main/provider-cctp-v2/node_modules/@noble/curves": { + "node_modules/@circle-fin/provider-cctp-v2/node_modules/@noble/curves": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", @@ -1384,7 +1300,7 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/@crcl-main/provider-cctp-v2/node_modules/@noble/hashes": { + "node_modules/@circle-fin/provider-cctp-v2/node_modules/@noble/hashes": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", @@ -1396,7 +1312,57 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/@crcl-main/provider-cctp-v2/node_modules/pino": { + "node_modules/@circle-fin/provider-cctp-v2/node_modules/pino": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-10.1.0.tgz", + "integrity": "sha512-0zZC2ygfdqvqK8zJIr1e+wT1T/L+LF6qvqvbzEQ6tiMAoTqEVK9a1K3YRu8HEUvGEvNqZyPJTtb2sNIoTkB83w==", + "license": "MIT", + "dependencies": { + "@pinojs/redact": "^0.4.0", + "atomic-sleep": "^1.0.0", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^2.0.0", + "pino-std-serializers": "^7.0.0", + "process-warning": "^5.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^4.0.1", + "thread-stream": "^3.0.0" + }, + "bin": { + "pino": "bin.js" + } + }, + "node_modules/@circle-fin/provider-cctp-v2/node_modules/zod": { + "version": "3.25.67", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.67.tgz", + "integrity": "sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@circle-fin/provider-cctpx": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@circle-fin/provider-cctpx/-/provider-cctpx-1.0.1.tgz", + "integrity": "sha512-mGbnNJTAj4H26NAZxZr8RyxQtMS2nNwWsl1hDzXau0OQARKieCQV/GIwhDXeVI+BzfgBbFLnQOtrAmkWV3owVA==", + "dependencies": { + "@ethersproject/abi": "^5.8.0", + "@ethersproject/address": "^5.8.0", + "@ethersproject/bytes": "^5.8.0", + "@ethersproject/units": "^5.8.0", + "@solana/web3.js": "^1.98.4", + "abitype": "^1.1.0", + "bs58": "6.0.0", + "pino": "10.1.0", + "zod": "3.25.67" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@circle-fin/provider-cctpx/node_modules/pino": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/pino/-/pino-10.1.0.tgz", "integrity": "sha512-0zZC2ygfdqvqK8zJIr1e+wT1T/L+LF6qvqvbzEQ6tiMAoTqEVK9a1K3YRu8HEUvGEvNqZyPJTtb2sNIoTkB83w==", @@ -1418,7 +1384,7 @@ "pino": "bin.js" } }, - "node_modules/@crcl-main/provider-cctp-v2/node_modules/zod": { + "node_modules/@circle-fin/provider-cctpx/node_modules/zod": { "version": "3.25.67", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.67.tgz", "integrity": "sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==", @@ -1427,10 +1393,10 @@ "url": "https://github.com/sponsors/colinhacks" } }, - "node_modules/@crcl-main/provider-earn-service": { - "version": "1.5.1-canary-feature-onramp-kit-sdk.1789055534", - "resolved": "https://npm.cloudsmith.io/circle/common-private/@crcl-main/provider-earn-service/-/provider-earn-service-1.5.1-canary-feature-onramp-kit-sdk.1789055534.tgz", - "integrity": "sha512-YwP9w5ly42cgW1GngCxCfVqLZh0s1INa2ZpzxgKEzsetKUvvIPhyVjGulY7czYeGj5KKHYZ2ZXQqrC9T5qLSpQ==", + "node_modules/@circle-fin/provider-earn-service": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@circle-fin/provider-earn-service/-/provider-earn-service-1.6.0.tgz", + "integrity": "sha512-XPhnAMHfANPdA/ENtF87xykKrp2TUezTc/jzJYzdagwV4JKpNmKN8XdJOUFNekS3QEff+ab7jyC131VywUrzIQ==", "dependencies": { "@ethersproject/abi": "^5.8.0", "@ethersproject/address": "^5.8.0", @@ -1447,7 +1413,7 @@ "node": ">=20.0.0" } }, - "node_modules/@crcl-main/provider-earn-service/node_modules/pino": { + "node_modules/@circle-fin/provider-earn-service/node_modules/pino": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/pino/-/pino-10.1.0.tgz", "integrity": "sha512-0zZC2ygfdqvqK8zJIr1e+wT1T/L+LF6qvqvbzEQ6tiMAoTqEVK9a1K3YRu8HEUvGEvNqZyPJTtb2sNIoTkB83w==", @@ -1469,7 +1435,7 @@ "pino": "bin.js" } }, - "node_modules/@crcl-main/provider-earn-service/node_modules/zod": { + "node_modules/@circle-fin/provider-earn-service/node_modules/zod": { "version": "3.25.67", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.67.tgz", "integrity": "sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==", @@ -1478,10 +1444,10 @@ "url": "https://github.com/sponsors/colinhacks" } }, - "node_modules/@crcl-main/provider-fee-v1": { - "version": "0.3.0-canary-feature-onramp-kit-sdk.1789055534", - "resolved": "https://npm.cloudsmith.io/circle/common-private/@crcl-main/provider-fee-v1/-/provider-fee-v1-0.3.0-canary-feature-onramp-kit-sdk.1789055534.tgz", - "integrity": "sha512-3TFMhB3BQgP632LEN7ZK1GiqeX2aTtwEXR7FdksKOjPwTCrfrzvH9D76aYGzLn4SBR9tV+nQjIQnzJwz5GWz8Q==", + "node_modules/@circle-fin/provider-fee-v1": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@circle-fin/provider-fee-v1/-/provider-fee-v1-0.3.1.tgz", + "integrity": "sha512-KHWU3pj7RLIw1QQ7EJE4SWaJbmEtuz0Vs9RjANWBkC+sI/PjxnzCTEOraFdNAK3QpxftZoDX21dqT6v/a0c4YA==", "dependencies": { "@ethersproject/abi": "^5.8.0", "@ethersproject/address": "^5.8.0", @@ -1494,7 +1460,7 @@ "node": ">=20.0.0" } }, - "node_modules/@crcl-main/provider-fee-v1/node_modules/zod": { + "node_modules/@circle-fin/provider-fee-v1/node_modules/zod": { "version": "3.25.67", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.67.tgz", "integrity": "sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==", @@ -1503,10 +1469,10 @@ "url": "https://github.com/sponsors/colinhacks" } }, - "node_modules/@crcl-main/provider-gateway-v1": { - "version": "1.4.0-canary-feature-onramp-kit-sdk.1789055534", - "resolved": "https://npm.cloudsmith.io/circle/common-private/@crcl-main/provider-gateway-v1/-/provider-gateway-v1-1.4.0-canary-feature-onramp-kit-sdk.1789055534.tgz", - "integrity": "sha512-dGq3koCcmVP+Zh1ez0dupDN/Sb0bMt7h+WfWQJGpMcJao/dfGjf3Dx8X366CWgxlYM/emD0RVPEYw7UcakJVxw==", + "node_modules/@circle-fin/provider-gateway-v1": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@circle-fin/provider-gateway-v1/-/provider-gateway-v1-1.5.0.tgz", + "integrity": "sha512-NiEISO6jKbc4fmDG1Qhd5vma5VA3S1o7uz/U4hW9L/5nAnW5LZJfO1SrfX1jHQfgnmvY79qR3Q7Qi0TGDbuzIg==", "dependencies": { "@coral-xyz/anchor": "^0.31.1", "@ethersproject/abi": "^5.8.0", @@ -1526,7 +1492,7 @@ "node": ">=20.0.0" } }, - "node_modules/@crcl-main/provider-gateway-v1/node_modules/@noble/curves": { + "node_modules/@circle-fin/provider-gateway-v1/node_modules/@noble/curves": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", @@ -1538,7 +1504,7 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/@crcl-main/provider-gateway-v1/node_modules/@noble/hashes": { + "node_modules/@circle-fin/provider-gateway-v1/node_modules/@noble/hashes": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", @@ -1550,7 +1516,7 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/@crcl-main/provider-gateway-v1/node_modules/pino": { + "node_modules/@circle-fin/provider-gateway-v1/node_modules/pino": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/pino/-/pino-10.1.0.tgz", "integrity": "sha512-0zZC2ygfdqvqK8zJIr1e+wT1T/L+LF6qvqvbzEQ6tiMAoTqEVK9a1K3YRu8HEUvGEvNqZyPJTtb2sNIoTkB83w==", @@ -1572,7 +1538,7 @@ "pino": "bin.js" } }, - "node_modules/@crcl-main/provider-gateway-v1/node_modules/zod": { + "node_modules/@circle-fin/provider-gateway-v1/node_modules/zod": { "version": "3.25.67", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.67.tgz", "integrity": "sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==", @@ -1581,10 +1547,10 @@ "url": "https://github.com/sponsors/colinhacks" } }, - "node_modules/@crcl-main/provider-stablecoin-service-swap": { - "version": "1.5.1-canary-feature-onramp-kit-sdk.1789055534", - "resolved": "https://npm.cloudsmith.io/circle/common-private/@crcl-main/provider-stablecoin-service-swap/-/provider-stablecoin-service-swap-1.5.1-canary-feature-onramp-kit-sdk.1789055534.tgz", - "integrity": "sha512-lgwYtOhKQ61kHOtkoV5YtPzLjSx5xrUrE2UwK4K0q84ecoMF6oS6r4q6SHNeopVHxakaDMHVM7ZHUghLGz2KEA==", + "node_modules/@circle-fin/provider-stablecoin-service-swap": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@circle-fin/provider-stablecoin-service-swap/-/provider-stablecoin-service-swap-1.6.0.tgz", + "integrity": "sha512-WVA6LY4rYgSCXoVd0ukbTQXjX9RwbKzoq8IJzr9kqSKUllTMMrf449G94WUZKVihSUGs4uPUkhlDBDH2ZVwRng==", "dependencies": { "@coral-xyz/anchor": "^0.31.1", "@ethersproject/address": "^5.8.0", @@ -1601,7 +1567,7 @@ "node": ">=20.0.0" } }, - "node_modules/@crcl-main/provider-stablecoin-service-swap/node_modules/@noble/curves": { + "node_modules/@circle-fin/provider-stablecoin-service-swap/node_modules/@noble/curves": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", @@ -1613,7 +1579,7 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/@crcl-main/provider-stablecoin-service-swap/node_modules/@noble/hashes": { + "node_modules/@circle-fin/provider-stablecoin-service-swap/node_modules/@noble/hashes": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", @@ -1625,7 +1591,7 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/@crcl-main/provider-stablecoin-service-swap/node_modules/zod": { + "node_modules/@circle-fin/provider-stablecoin-service-swap/node_modules/zod": { "version": "3.25.67", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.67.tgz", "integrity": "sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==", @@ -1634,13 +1600,13 @@ "url": "https://github.com/sponsors/colinhacks" } }, - "node_modules/@crcl-main/swap-kit": { - "version": "1.6.1-canary-feature-onramp-kit-sdk.1789055534", - "resolved": "https://npm.cloudsmith.io/circle/common-private/@crcl-main/swap-kit/-/swap-kit-1.6.1-canary-feature-onramp-kit-sdk.1789055534.tgz", - "integrity": "sha512-I6VqJ5oml3Rh2SnE/VHm/B17KFFNx6naMuYv1Hgaem783e7812lSlWZ17dwxMz2wnJ2/CNzzM6vrrMn0BOQkSQ==", + "node_modules/@circle-fin/swap-kit": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@circle-fin/swap-kit/-/swap-kit-1.7.0.tgz", + "integrity": "sha512-kzACxFS+x7QEtFNE/UBe9A+RjeUDN5vKws6ztXA5XaBlazIqIMQwvXNfAiBlCtBmcYTLihsAKA7RARBiwQ9SaQ==", "dependencies": { + "@circle-fin/provider-stablecoin-service-swap": "^1.6.0", "@coral-xyz/anchor": "^0.31.1", - "@crcl-main/provider-stablecoin-service-swap": "1.5.1-canary-feature-onramp-kit-sdk.1789055534", "@ethersproject/abi": "^5.8.0", "@ethersproject/address": "^5.8.0", "@ethersproject/bytes": "^5.8.0", @@ -1656,7 +1622,7 @@ "node": ">=20.0.0" } }, - "node_modules/@crcl-main/swap-kit/node_modules/@noble/curves": { + "node_modules/@circle-fin/swap-kit/node_modules/@noble/curves": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", @@ -1668,7 +1634,7 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/@crcl-main/swap-kit/node_modules/@noble/hashes": { + "node_modules/@circle-fin/swap-kit/node_modules/@noble/hashes": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", @@ -1680,7 +1646,7 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/@crcl-main/swap-kit/node_modules/zod": { + "node_modules/@circle-fin/swap-kit/node_modules/zod": { "version": "3.25.67", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.67.tgz", "integrity": "sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==", @@ -1689,15 +1655,15 @@ "url": "https://github.com/sponsors/colinhacks" } }, - "node_modules/@crcl-main/unified-balance-kit": { - "version": "1.6.0-canary-feature-onramp-kit-sdk.1789055534", - "resolved": "https://npm.cloudsmith.io/circle/common-private/@crcl-main/unified-balance-kit/-/unified-balance-kit-1.6.0-canary-feature-onramp-kit-sdk.1789055534.tgz", - "integrity": "sha512-fFOiusRzjGdXcti/bOkMu1aPYQowtC9VJ9QxCaBjfoIT8hf/szl9k/i0DAEpFZeTWRcOiiGznH8orM/hjoNF1A==", + "node_modules/@circle-fin/unified-balance-kit": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@circle-fin/unified-balance-kit/-/unified-balance-kit-1.7.0.tgz", + "integrity": "sha512-VEp7bUhQLs2x7yc9+Ll39inxRsOXJJBDvn585ur1YcGlqo3tKoAZ+lCB6mLplTDC3xjnSxKijKBE81Rga8l4Bg==", "dependencies": { + "@circle-fin/provider-cctp-v2": "1.14.0", + "@circle-fin/provider-fee-v1": "0.3.1", + "@circle-fin/provider-gateway-v1": "1.5.0", "@coral-xyz/anchor": "^0.31.1", - "@crcl-main/provider-cctp-v2": "1.13.0-canary-feature-onramp-kit-sdk.1789055534", - "@crcl-main/provider-fee-v1": "0.3.0-canary-feature-onramp-kit-sdk.1789055534", - "@crcl-main/provider-gateway-v1": "1.4.0-canary-feature-onramp-kit-sdk.1789055534", "@ethersproject/abi": "^5.8.0", "@ethersproject/address": "^5.8.0", "@ethersproject/bytes": "^5.8.0", @@ -1714,7 +1680,7 @@ "node": ">=20.0.0" } }, - "node_modules/@crcl-main/unified-balance-kit/node_modules/@noble/curves": { + "node_modules/@circle-fin/unified-balance-kit/node_modules/@noble/curves": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", @@ -1726,7 +1692,7 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/@crcl-main/unified-balance-kit/node_modules/@noble/hashes": { + "node_modules/@circle-fin/unified-balance-kit/node_modules/@noble/hashes": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", @@ -1738,7 +1704,7 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/@crcl-main/unified-balance-kit/node_modules/uuid": { + "node_modules/@circle-fin/unified-balance-kit/node_modules/uuid": { "version": "14.0.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.2.tgz", "integrity": "sha512-xZe/16rV4aa+HGSOCiY2YeLT1OybRLrrkL/Rqaq7p7GMVXjFh+6wN4oMYgjFmnSnhY8t6Xpdl2l9qmnHYuMHwQ==", @@ -1751,7 +1717,7 @@ "uuid": "dist-node/bin/uuid" } }, - "node_modules/@crcl-main/unified-balance-kit/node_modules/zod": { + "node_modules/@circle-fin/unified-balance-kit/node_modules/zod": { "version": "3.25.67", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.67.tgz", "integrity": "sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==", @@ -1760,6 +1726,91 @@ "url": "https://github.com/sponsors/colinhacks" } }, + "node_modules/@coral-xyz/anchor": { + "version": "0.31.1", + "resolved": "https://registry.npmjs.org/@coral-xyz/anchor/-/anchor-0.31.1.tgz", + "integrity": "sha512-QUqpoEK+gi2S6nlYc2atgT2r41TT3caWr/cPUEL8n8Md9437trZ68STknq897b82p5mW0XrTBNOzRbmIRJtfsA==", + "license": "(MIT OR Apache-2.0)", + "dependencies": { + "@coral-xyz/anchor-errors": "^0.31.1", + "@coral-xyz/borsh": "^0.31.1", + "@noble/hashes": "^1.3.1", + "@solana/web3.js": "^1.69.0", + "bn.js": "^5.1.2", + "bs58": "^4.0.1", + "buffer-layout": "^1.2.2", + "camelcase": "^6.3.0", + "cross-fetch": "^3.1.5", + "eventemitter3": "^4.0.7", + "pako": "^2.0.3", + "superstruct": "^0.15.4", + "toml": "^3.0.0" + }, + "engines": { + "node": ">=17" + } + }, + "node_modules/@coral-xyz/anchor-errors": { + "version": "0.31.1", + "resolved": "https://registry.npmjs.org/@coral-xyz/anchor-errors/-/anchor-errors-0.31.1.tgz", + "integrity": "sha512-NhNEku4F3zzUSBtrYz84FzYWm48+9OvmT1Hhnwr6GnPQry2dsEqH/ti/7ASjjpoFTWRnPXrjAIT1qM6Isop+LQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=10" + } + }, + "node_modules/@coral-xyz/anchor/node_modules/base-x": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.11.tgz", + "integrity": "sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/@coral-xyz/anchor/node_modules/bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", + "license": "MIT", + "dependencies": { + "base-x": "^3.0.2" + } + }, + "node_modules/@coral-xyz/anchor/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@coral-xyz/anchor/node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "license": "MIT" + }, + "node_modules/@coral-xyz/borsh": { + "version": "0.31.1", + "resolved": "https://registry.npmjs.org/@coral-xyz/borsh/-/borsh-0.31.1.tgz", + "integrity": "sha512-9N8AU9F0ubriKfNE3g1WF0/4dtlGXoBN/hd1PvbNBamBNwRgHxH4P+o3Zt7rSEloW1HUs6LfZEchlx9fW7POYw==", + "license": "Apache-2.0", + "dependencies": { + "bn.js": "^5.1.2", + "buffer-layout": "^1.2.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@solana/web3.js": "^1.69.0" + } + }, "node_modules/@dotenvx/dotenvx": { "version": "1.75.1", "resolved": "https://registry.npmjs.org/@dotenvx/dotenvx/-/dotenvx-1.75.1.tgz", @@ -1954,21 +2005,21 @@ "license": "BSD-3-Clause" }, "node_modules/@emnapi/core": { - "version": "1.11.2", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.2.tgz", - "integrity": "sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA==", + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.3.tgz", + "integrity": "sha512-zLpS5asjEb7lq8jYLq37N6XKaE41DIexlY1rF/z4/tIl3wo13Sqm28fRyfIsKZD+NZ8mM5RoKkpW/rBcuoSZSg==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@emnapi/wasi-threads": "1.2.2", + "@emnapi/wasi-threads": "1.2.3", "tslib": "^2.4.0" } }, "node_modules/@emnapi/runtime": { - "version": "1.11.2", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.2.tgz", - "integrity": "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==", + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz", + "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==", "dev": true, "license": "MIT", "optional": true, @@ -1977,9 +2028,9 @@ } }, "node_modules/@emnapi/wasi-threads": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", - "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.3.tgz", + "integrity": "sha512-ELEBe8PsLvvJ6QMr0zLt8ffvOHW/dc1m3CEzNMg7aJUv3bMaoDtw2TXyDAwkYBuroxxuHEwhRTLJSe5sya547g==", "dev": true, "license": "MIT", "optional": true, @@ -4479,6 +4530,40 @@ "node": ">=14.0.0" } }, + "node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.2.tgz", + "integrity": "sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.2.tgz", + "integrity": "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@emnapi/wasi-threads": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@oxc-resolver/binding-win32-arm64-msvc": { "version": "11.24.2", "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.24.2.tgz", @@ -7479,27 +7564,6 @@ "node": ">=14.0.0" } }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { - "version": "1.11.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.2", - "tslib": "^2.4.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { - "version": "1.11.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { "version": "1.2.2", "dev": true, @@ -9118,20 +9182,6 @@ "node": ">=4.5" } }, - "node_modules/bufferutil": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.1.0.tgz", - "integrity": "sha512-ZMANVnAixE6AWWnPzlW2KpUrxhm9woycYvPOo67jWHyFowASTEd9s+QN1EIMsSDtwhIxN4sWE1jotpuDUIgyIw==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "dependencies": { - "node-gyp-build": "^4.3.0" - }, - "engines": { - "node": ">=6.14.2" - } - }, "node_modules/bundle-name": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", diff --git a/package.json b/package.json index 266a27e..4bbe18d 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "lint": "tsc -p tsconfig.json --noEmit", "test": "tsx --tsconfig tsconfig.test.json --test \"test/**/*.test.ts\"", "preview": "vite preview", - "dockerize": "docker build --secret id=cloudsmith_token,env=CLOUDSMITH_TOKEN -t oneshot-wallet .", + "dockerize": "docker build -t oneshot-wallet .", "doctor": "npx react-doctor@latest" }, "dependencies": { @@ -30,7 +30,7 @@ "@1shotapi/ows-signer-utils": "^0.6.2", "@1shotapi/ows-types": "^0.8.0", "@1shotapi/ows-wallet-utils": "^0.5.1", - "@crcl-main/app-kit": "^1.14.0-canary-feature-onramp-kit-sdk.1789055534", + "@circle-fin/app-kit": "^1.15.2", "@fontsource-variable/geist": "^5.3.0", "@metamask/smart-accounts-kit": "^2.0.0", "@reown/walletkit": "^1.5.6", diff --git a/skills/oneshot-embedded-wallet/SKILL.md b/skills/oneshot-embedded-wallet/SKILL.md index ae368ad..11113e7 100644 --- a/skills/oneshot-embedded-wallet/SKILL.md +++ b/skills/oneshot-embedded-wallet/SKILL.md @@ -111,12 +111,26 @@ await proxy.rpc("configure", options); | `copy.personalSign.rejectLabel` | string | Reject button | | `copy.personalSign.signLabel` | string | Sign button | | `copy.typedData.title` | string | EIP-712 modal title | -| `copy.typedData.accountLabel` | string | Account field label | +| `copy.typedData.body` | string | Intro paragraph | +| `copy.typedData.networkLabel` | string | Network summary row label | +| `copy.typedData.requestFromLabel` | string | Requesting domain row label | +| `copy.typedData.accountLabel` | string | Signing account row label | +| `copy.typedData.interactingWithLabel` | string | Verifying contract row label | | `copy.typedData.primaryTypeLabel` | string | Primary type label | -| `copy.typedData.domainLabel` | string | Domain label | -| `copy.typedData.messageLabel` | string | Message label | +| `copy.typedData.messageSectionLabel` | string | Message card heading | +| `copy.typedData.signingHint` | string | Hint shown while signing | | `copy.typedData.rejectLabel` | string | Reject button | | `copy.typedData.signLabel` | string | Sign button | +| `copy.grantExecutionPermission.title` | string | ERC-20 periodic grant modal title | +| `copy.grantExecutionPermission.permissionKindLabel` | string | Summary card eyebrow | +| `copy.grantExecutionPermission.amountLabel` | string | Summary amount row | +| `copy.grantExecutionPermission.transferWindowLabel` | string | Summary cadence row | +| `copy.grantExecutionPermission.toLabel` | string | Delegate summary row | +| `copy.grantExecutionPermission.viewOnExplorerLabel` | string | Delegate explorer link a11y | +| `copy.grantExecutionPermission.justificationLabel` | string | Host justification in terms card | +| `copy.grantExecutionPermission.advancedLabel` | string | Toggle for Unix start field | +| `copy.grantExecutionPermission.grantLabel` | string | Grant button | +| `copy.grantExecutionPermission.rejectLabel` | string | Reject button | | `copy.credentialOffer.title` | string | offer modal title | | `copy.credentialOffer.body` | string | supports `{issuerName}` `{issuerId}` | | `copy.credentialOffer.offeredHeading` | string | offered list heading | @@ -356,8 +370,8 @@ EIP-7715 host RPCs: `wallet_requestExecutionPermissions`, `wallet_revokeExecutio | `permission.type` | Chains (v1) | Purpose | |-------------------|-------------|---------| | `erc20-token-periodic` | All relayer chains | Periodic ERC-20 `transfer` budget (`ScopeType.Erc20PeriodTransfer`) | -| `lifi-swap-approve` | Base (`0x2105`) | One-time `approve(inputToken → LiFi Diamond)` onboarding | -| `lifi-swap-periodic` | Base (`0x2105`) | Periodic LiFi swap via `LiFiSwapEnforcer` (`0x47472E8AA7012D1c23336aa28514AE94389318f5`) | +| `lifi-swap-approve` | Arc (`0x13b2`), Base (`0x2105`), Ethereum (`0x1`) | One-time `approve(inputToken → LiFi Diamond)` onboarding | +| `lifi-swap-periodic` | Arc, Base, Ethereum mainnet | Periodic LiFi swap via `LiFiSwapEnforcer` proxy (`0x29fcBBa852439616c4D614A2fa6411E42b760153`) | **`erc20-token-periodic` / `lifi-swap-periodic` amounts:** Hosts should pass `periodAmount` (hex atoms) to prefill the grant form. Playground demos default to **10 USDC** (`0x989680`). diff --git a/src/assets/images/platforms/circle-logo-stacked.svg b/src/assets/images/platforms/circle-logo-stacked.svg new file mode 100644 index 0000000..fadb576 --- /dev/null +++ b/src/assets/images/platforms/circle-logo-stacked.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/circle/onrampTypes.ts b/src/circle/onrampTypes.ts index 5c2b61d..2f67736 100644 --- a/src/circle/onrampTypes.ts +++ b/src/circle/onrampTypes.ts @@ -6,4 +6,6 @@ export type IOnrampOpenRequest = { chainId?: number; amount?: string; tokenSymbol?: string; + tokenAddress?: EVMAccountAddress; + iconUrl?: string; }; diff --git a/src/components/AssetDetails.tsx b/src/components/AssetDetails.tsx index f7f72f9..9c78da5 100644 --- a/src/components/AssetDetails.tsx +++ b/src/components/AssetDetails.tsx @@ -124,6 +124,8 @@ export function AssetDetails({ asset: assetProp }: IAssetDetailsProps) { destinationAddress: evmAddress, chainId: Number(BigInt(asset.chainId)), tokenSymbol: asset.symbol, + tokenAddress: asset.address, + iconUrl: asset.iconUrl, }) .catch(() => { /* user closed or mint failed — OnrampView surfaces errors */ diff --git a/src/components/ConsentSummaryRow.tsx b/src/components/ConsentSummaryRow.tsx new file mode 100644 index 0000000..843e665 --- /dev/null +++ b/src/components/ConsentSummaryRow.tsx @@ -0,0 +1,21 @@ +import type { ReactNode } from "react"; + +/** Label-left / value-right row for consent summary cards (typed data, permissions). */ +export function ConsentSummaryRow({ + label, + children, +}: { + label: string; + children: ReactNode; +}) { + return ( +
+
+ {label} +
+
+ {children} +
+
+ ); +} diff --git a/src/components/Eip712FieldTree.tsx b/src/components/Eip712FieldTree.tsx new file mode 100644 index 0000000..d90231f --- /dev/null +++ b/src/components/Eip712FieldTree.tsx @@ -0,0 +1,115 @@ +import type { ReactNode } from "react"; +import { + formatEip712Primitive, + humanizeEip712Key, + isEip712NestedValue, + isEvmAddressString, +} from "../lib/utils/eip712Display"; +import { truncateAddress } from "../lib/utils/identityDisplay"; + +const MAX_DEPTH = 12; + +export type IEip712FieldTreeProps = { + value: unknown; + depth?: number; + label?: string; +}; + +/** Renders a resolved EIP-712 `message` as nested label / value rows. */ +export function Eip712FieldTree({ + value, + depth = 0, + label, +}: IEip712FieldTreeProps) { + if (depth > MAX_DEPTH) { + return ( + + ); + } + + if (Array.isArray(value)) { + return ( + + {value.map((item, index) => ( + + ))} + + ); + } + + if (isEip712NestedValue(value)) { + return ( + + {Object.keys(value as Record).map((key) => ( + )[key]} + depth={depth + 1} + label={humanizeEip712Key(key)} + /> + ))} + + ); + } + + return ( + + ); +} + +function Eip712Group({ + label, + children, +}: { + label?: string; + children: ReactNode; +}) { + if (!label) { + return
{children}
; + } + return ( +
+

{label}

+
+ {children} +
+
+ ); +} + +function Eip712PrimitiveRow({ + label, + value, +}: { + label: string; + value: string; +}) { + if (!value) { + return null; + } + const isAddress = isEvmAddressString(value); + return ( +
+ + {label} + + + {isAddress ? truncateAddress(value) : value} + +
+ ); +} diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx index 9b8d09a..0c78c0e 100644 --- a/src/components/Modal.tsx +++ b/src/components/Modal.tsx @@ -20,7 +20,7 @@ export type ModalAction = { export type ModalPresentation = "page" | "overlay"; export type ModalProps = { - title: string; + title: ReactNode; children: ReactNode; actions?: ModalAction[]; /** Escape / overlay dismiss. Omit to lock until an action. */ diff --git a/src/components/ModalHost.tsx b/src/components/ModalHost.tsx index c8335d9..f390c72 100644 --- a/src/components/ModalHost.tsx +++ b/src/components/ModalHost.tsx @@ -22,9 +22,7 @@ import { AddAssetModal } from "./modals/AddAssetModal"; import { OpenCreateTabModal } from "./modals/OpenCreateTabModal"; import { OnrampView } from "./OnrampView"; import { CCTPBridge } from "./modals/CCTPBridge"; -import { GrantExecutionPermissionModal } from "./modals/GrantExecutionPermissionModal"; -import { GrantLiFiSwapPermissionModal } from "./modals/GrantLiFiSwapPermissionModal"; -import { GrantLiFiApprovePermissionModal } from "./modals/GrantLiFiApprovePermissionModal"; +import { GrantPermissionConsentModal } from "./modals/GrantPermissionConsentModal"; import { CancelDelegationModal } from "./modals/CancelDelegationModal"; export function ModalHost() { @@ -103,25 +101,9 @@ export function ModalHost() { onResolve={activeModal.resolve} /> ); - case "grantExecutionPermission": + case "grantExecutionPermissions": return ( - - ); - case "grantLiFiSwapPermission": - return ( - - ); - case "grantLiFiApprovePermission": - return ( - activeModal.resolve()} /> ); diff --git a/src/components/OnrampView.tsx b/src/components/OnrampView.tsx index c77c53d..113d469 100644 --- a/src/components/OnrampView.tsx +++ b/src/components/OnrampView.tsx @@ -1,20 +1,34 @@ -import { useEffect, useRef, useState } from "react"; +import { useEffect, useRef, useState, type ReactNode } from "react"; import type { AppKitOnrampOperations, OnrampSession, OnrampWidget, -} from "@crcl-main/app-kit"; -import type { EVMAccountAddress } from "@1shotapi/ows-types"; +} from "@circle-fin/app-kit"; +import { + ChainUtils, + EVMAccountAddress, + EVMChainId, + type EVMAccountAddress as EVMAccountAddressType, +} from "@1shotapi/ows-types"; +import { zeroAddress } from "viem"; import { Modal, type ModalAction } from "./Modal"; import { useCircle } from "../circle/CircleContext"; import { circleChainLabelFromChainId } from "../circle/circleChains"; import { isCirclePopupPreferred } from "../circle/circlePopup"; import type { IOnrampOpenRequest } from "../circle/onrampTypes"; +import { useStyle } from "../style/StyleProvider"; +import { useWallet } from "../wallet/WalletProvider"; +import { useWalletSessionStore } from "../wallet/sessionStore"; +import { AssetIdentityMark } from "./AssetIdentityMark"; +import { CopyableText } from "./CopyableText"; +import circleLogoStacked from "../assets/images/platforms/circle-logo-stacked.svg"; export type IOnrampViewProps = IOnrampOpenRequest & { onClose: () => void; }; +const PLACEHOLDER_TOKEN_ADDRESS = EVMAccountAddress(zeroAddress); + /** * Full-screen Circle AppKit onramp inside the Branding Layer shell. * Nested in a Host iframe (or `localStorage.circlePopup === "true"`): popup @@ -23,12 +37,20 @@ export type IOnrampViewProps = IOnrampOpenRequest & { */ export function OnrampView({ destinationAddress, - chainId, + chainId: chainIdProp, amount, - tokenSymbol, + tokenSymbol: tokenSymbolProp, + tokenAddress: tokenAddressProp, + iconUrl: iconUrlProp, onClose, }: IOnrampViewProps) { const circle = useCircle(); + const { style } = useStyle(); + const { resolveChain, knownAssetRepository } = useWallet(); + const sessionChainId = useWalletSessionStore((state) => state.chainId); + const copy = style.copy.onramp; + const accountCopy = style.copy.account; + const usePopup = isCirclePopupPreferred(); const containerRef = useRef(null); const widgetRef = useRef(null); @@ -38,12 +60,70 @@ export function OnrampView({ const [loading, setLoading] = useState(true); const [popupReady, setPopupReady] = useState(false); const [popupOpened, setPopupOpened] = useState(false); + const [catalogTokenAddress, setCatalogTokenAddress] = useState< + EVMAccountAddressType | null + >(null); + const [catalogIconUrl, setCatalogIconUrl] = useState(); + + const effectiveEvmChainId = resolveEffectiveEvmChainId( + chainIdProp, + sessionChainId, + ); + const tokenSymbol = (tokenSymbolProp?.trim() || "USDC").toUpperCase(); + const chain = effectiveEvmChainId + ? resolveChain(effectiveEvmChainId) + : null; + const networkLabel = chain?.label ?? "your network"; + const body = copy.body + .replaceAll("{token}", tokenSymbol) + .replaceAll("{network}", networkLabel); + + const tokenAddress = + tokenAddressProp ?? catalogTokenAddress ?? PLACEHOLDER_TOKEN_ADDRESS; + const iconUrl = iconUrlProp ?? catalogIconUrl; + + useEffect(() => { + if (tokenAddressProp || !effectiveEvmChainId) { + setCatalogTokenAddress(null); + setCatalogIconUrl(undefined); + return; + } + + let cancelled = false; + void knownAssetRepository + .getOnrampAsset(effectiveEvmChainId, tokenSymbol) + .then((asset) => { + if (cancelled) return; + if (asset) { + setCatalogTokenAddress(asset.address); + setCatalogIconUrl(asset.iconUrl); + } else { + setCatalogTokenAddress(null); + setCatalogIconUrl(undefined); + } + }) + .catch(() => { + if (!cancelled) { + setCatalogTokenAddress(null); + setCatalogIconUrl(undefined); + } + }); + + return () => { + cancelled = true; + }; + }, [ + effectiveEvmChainId, + knownAssetRepository, + tokenAddressProp, + tokenSymbol, + ]); useEffect(() => { let cancelled = false; const body = buildSessionBody({ destinationAddress, - chainId, + chainId: chainIdProp ?? decimalChainIdFromEvm(effectiveEvmChainId), amount, tokenSymbol, }); @@ -136,7 +216,15 @@ export function OnrampView({ widgetRef.current?.close(); widgetRef.current = null; }; - }, [amount, chainId, circle, destinationAddress, tokenSymbol, usePopup]); + }, [ + amount, + chainIdProp, + circle, + destinationAddress, + effectiveEvmChainId, + tokenSymbol, + usePopup, + ]); const openPopup = () => { const onramp = onrampRef.current; @@ -155,13 +243,16 @@ export function OnrampView({ void (async () => { try { const url = await circle.getSessionUrl(); - const body = buildSessionBody({ + const sessionBody = buildSessionBody({ destinationAddress, - chainId, + chainId: chainIdProp ?? decimalChainIdFromEvm(effectiveEvmChainId), amount, tokenSymbol, }); - sessionRef.current = await onramp.fetchSession({ url, body }); + sessionRef.current = await onramp.fetchSession({ + url, + body: sessionBody, + }); setLoading(false); setPopupReady(true); } catch (err: unknown) { @@ -188,51 +279,103 @@ export function OnrampView({ setPopupOpened(true); }; - const actions: ModalAction[] = []; + const actions: ModalAction[] = [ + { label: copy.closeLabel, onClick: onClose, variant: "secondary" }, + ]; if (usePopup && popupReady) { actions.push({ - label: popupOpened ? "Reopen onramp" : "Open onramp", + label: popupOpened ? copy.reopenLabel : copy.openLabel, onClick: openPopup, variant: "primary", + autoFocus: true, }); } - actions.push({ label: "Close", onClick: onClose, variant: "secondary" }); + + const statusMessage = (() => { + if (error) return null; + if (loading) { + return usePopup ? copy.preparingLabel : copy.loadingLabel; + } + if (usePopup && popupReady && !popupOpened) { + return copy.popupReadyBody; + } + if (usePopup && popupOpened) { + return copy.popupOpenedBody; + } + return null; + })(); return ( + + {copy.title} + + } onBackdropDismiss={onClose} contentClassName="z-50" actions={actions} > -
+
+

+ {renderOnrampBody(body, tokenSymbol)} +

+ + {effectiveEvmChainId ? ( +
+ +
+ + {amount ? `${amount} ` : ""} + {tokenSymbol} + + {chain ? ( + + {chain.label} + + ) : null} +
+
+ ) : null} + +
+ + {copy.destinationLabel} + + +
+ {error ? (

{error}

) : null} - {loading && !error ? ( -

- {usePopup ? "Preparing onramp…" : "Loading onramp…"} -

- ) : null} - {usePopup && popupReady && !popupOpened && !error ? ( -

- Circle opens in a popup (required when the wallet is host-iframed, or - for local/ngrok CSP bypass). Click Open onramp — browsers block - popups after an async delay. -

- ) : null} - {usePopup && popupOpened && !error ? ( -

- Onramp opened in a popup. Complete the purchase there, then close - this dialog. -

+ {statusMessage ? ( +

{statusMessage}

) : null} + {!usePopup ? (
) : null} @@ -241,6 +384,51 @@ export function OnrampView({ ); } +function resolveEffectiveEvmChainId( + chainIdProp: number | undefined, + sessionChainId: ReturnType["chainId"], +): EVMChainId | null { + if (chainIdProp != null && Number.isFinite(chainIdProp)) { + return EVMChainId(`0x${chainIdProp.toString(16)}` as `0x${string}`); + } + if (ChainUtils.isEVMChainId(sessionChainId)) { + return sessionChainId; + } + return null; +} + +function decimalChainIdFromEvm(chainId: EVMChainId | null): number | undefined { + if (!chainId) { + return undefined; + } + return Number(BigInt(chainId)); +} + +/** Keep “{token} 1-to-1” on one line even when hosts customize `copy.onramp.body`. */ +function renderOnrampBody(text: string, tokenSymbol: string): ReactNode { + const oneToOne = "1[\u2011-]to[\u2011-]1"; + const tokenUnit = `${escapeRegExp(tokenSymbol)}\\s+${oneToOne}`; + const pattern = new RegExp(`(${tokenUnit}|${oneToOne})`, "gi"); + const isNoBreakPart = new RegExp(`^(${tokenUnit}|${oneToOne})$`, "i"); + const parts = text.split(pattern); + if (parts.length === 1) { + return text; + } + return parts.map((part, index) => + isNoBreakPart.test(part) ? ( + + {part} + + ) : ( + part + ), + ); +} + +function escapeRegExp(value: string): string { + return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); +} + function buildSessionBody(request: { destinationAddress: EVMAccountAddress; chainId?: number; diff --git a/src/components/PaymentFeePicker.tsx b/src/components/PaymentFeePicker.tsx index 3cd8d95..dd70b54 100644 --- a/src/components/PaymentFeePicker.tsx +++ b/src/components/PaymentFeePicker.tsx @@ -1,7 +1,11 @@ -import { useCallback, useEffect, useRef, useState } from "react"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import type { EVMAccountAddress, EVMChainId } from "@1shotapi/ows-types"; import { formatUnits } from "viem"; -import type { IPaymentQuote, IPaymentTokenOption } from "../lib/interfaces/business"; +import type { + IPaymentQuote, + IPaymentTokenOption, + ITransactionWork, +} from "../lib/interfaces/business"; import type { IFinalRelayerFee } from "../lib/types/domain/RelayerSendUi"; import { useWallet } from "../wallet/WalletProvider"; import { AssetIcon } from "./AssetIcon"; @@ -19,6 +23,8 @@ export type IPaymentFeePickerMode = "estimate" | "final"; export interface IPaymentFeePickerProps { chainId: EVMChainId; ownerAddress: EVMAccountAddress; + /** ExactCalldata work used for unsigned `relayer_estimate7710Transaction`. */ + work: ITransactionWork | ITransactionWork[]; quote: IPaymentQuote | null; error: string | null; loading: boolean; @@ -66,12 +72,13 @@ function PaymentTokenRow({ /** * Loads payment-token options (USDC preferred) and shows a live fee quote - * with auto-refresh. Exact fee is settled by `relayer_estimate7710Transaction` - * at submit; use mode `final` to show the relayer-settled amount. + * from unsigned `relayer_estimate7710Transaction`. Use mode `final` after the + * signed estimate settles the amount at submit. */ export function PaymentFeePicker({ chainId, ownerAddress, + work, quote, error, loading, @@ -90,15 +97,34 @@ export function PaymentFeePicker({ onQuoteChangeRef.current = onQuoteChange; }, [onQuoteChange]); + const workKey = useMemo(() => { + const items = Array.isArray(work) ? work : [work]; + return items + .map( + (item) => + `${String(item.to)}:${String(item.data || "0x")}:${item.value ?? 0n}`, + ) + .join("|"); + }, [work]); + const getNewQuote = useCallback(async (): Promise => { - const next = await transactionService.quotePayment( - chainId, - ownerAddress, - preferredToken, - ); - onQuoteChangeRef.current(next, null); - return next.feeFormatted; - }, [chainId, ownerAddress, preferredToken, transactionService]); + try { + const next = await transactionService.quotePayment( + chainId, + ownerAddress, + work, + preferredToken, + ); + onQuoteChangeRef.current(next, null); + return next.feeFormatted; + } catch (err: unknown) { + onQuoteChangeRef.current( + null, + err instanceof Error ? err.message : "Failed to quote fee", + ); + throw err; + } + }, [chainId, ownerAddress, preferredToken, transactionService, work]); async function onSelectToken(token: EVMAccountAddress): Promise { setSelectBusy(true); @@ -107,6 +133,7 @@ export function PaymentFeePicker({ const next = await transactionService.quotePayment( chainId, ownerAddress, + work, token, ); onQuoteChange(next, null); @@ -149,7 +176,7 @@ export function PaymentFeePicker({ {feeDisplay} ) : ( diff --git a/src/components/RelayerConfirmModalChrome.tsx b/src/components/RelayerConfirmModalChrome.tsx index 0bea72d..b729464 100644 --- a/src/components/RelayerConfirmModalChrome.tsx +++ b/src/components/RelayerConfirmModalChrome.tsx @@ -1,4 +1,5 @@ import type { EVMAccountAddress, EVMChainId } from "@1shotapi/ows-types"; +import type { ITransactionWork } from "../lib/interfaces/business"; import { PaymentFeePicker } from "./PaymentFeePicker"; import type { useRelayerConfirmSubmit } from "./useRelayerConfirmSubmit"; @@ -8,10 +9,12 @@ type RelayerSubmitState = ReturnType; export function RelayerConfirmModalChrome({ chainId, ownerAddress, + work, submit, }: { chainId: EVMChainId; ownerAddress: EVMAccountAddress; + work: ITransactionWork | ITransactionWork[]; submit: RelayerSubmitState; }) { return ( @@ -24,6 +27,7 @@ export function RelayerConfirmModalChrome({ vars[key] ?? ""); } -function truncateAddress(address: string): string { - if (address.length <= 13) return address; - return `${address.slice(0, 6)}…${address.slice(-4)}`; -} - function formatDuration(seconds: number): string { if (seconds % 86_400 === 0) { const days = seconds / 86_400; @@ -38,10 +34,6 @@ function formatDuration(seconds: number): string { return seconds === 1 ? "1 second" : `${seconds} seconds`; } -function faviconUrl(domain: string): string { - return `https://www.google.com/s2/favicons?domain=${encodeURIComponent(domain)}&sz=32`; -} - interface IDelegationGroup { hostDomain: string; rows: IDelegationSummary[]; diff --git a/src/components/modals/CCTPBridge.tsx b/src/components/modals/CCTPBridge.tsx index 94bf4bb..16cac04 100644 --- a/src/components/modals/CCTPBridge.tsx +++ b/src/components/modals/CCTPBridge.tsx @@ -354,6 +354,8 @@ export function CCTPBridge({ owner: request.ownerAddress, }); setIrisQuote(next); + setPaymentQuote(next.paymentQuote); + setPaymentError(null); if (!opts.silent) { setPhase("quoted"); } @@ -793,6 +795,7 @@ export function CCTPBridge({ diff --git a/src/components/modals/GrantExecutionPermissionModal.tsx b/src/components/modals/GrantExecutionPermissionModal.tsx deleted file mode 100644 index 56b53c7..0000000 --- a/src/components/modals/GrantExecutionPermissionModal.tsx +++ /dev/null @@ -1,396 +0,0 @@ -import { useEffect, useMemo, useRef, useState } from "react"; -import { - EVMAccountAddress, - OwsUserRejectedError, - type IExecutionPermission, -} from "@1shotapi/ows-types"; -import { formatUnits, getAddress, hexToBigInt, parseUnits } from "viem"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; -import { Textarea } from "@/components/ui/textarea"; -import { ERC20_TOKEN_PERIODIC } from "../../lib/interfaces/business/IDelegationService"; -import { EAssetType } from "../../lib/types/enum/EAssetType"; -import { useStyle } from "../../style/StyleProvider"; -import type { - IGrantExecutionPermissionRequest, - IGrantExecutionPermissionResult, -} from "../../wallet/modalTypes"; -import { useWallet } from "../../wallet/WalletProvider"; -import { Modal } from "../Modal"; -import { TokenAmountInput } from "../TokenAmountInput"; -import { CopyableText } from "../CopyableText"; - -function readTokenAddress(data: Record): string | null { - const raw = data.tokenAddress ?? data.token; - return typeof raw === "string" ? raw : null; -} - -function readAmountAtoms(data: Record): bigint | null { - const raw = data.periodAmount ?? data.amount; - if (raw === undefined || raw === null) return null; - try { - if (typeof raw === "bigint") return raw; - if (typeof raw === "number") return BigInt(raw); - if (typeof raw === "string") { - const trimmed = raw.trim(); - if (trimmed.startsWith("0x") || trimmed.startsWith("0X")) { - return hexToBigInt(trimmed as `0x${string}`); - } - return BigInt(trimmed); - } - } catch { - return null; - } - return null; -} - -function readInitialMemo(data: Record): string { - const raw = data.justification; - return typeof raw === "string" ? raw : ""; -} - -function readDuration(data: Record): string { - const raw = data.periodDuration ?? data.period ?? data.duration; - if (typeof raw === "number" || typeof raw === "string") { - return String(raw); - } - return "86400"; -} - -function readStart(data: Record): string { - const raw = data.startDate ?? data.start; - if (typeof raw === "number" || typeof raw === "string") { - return String(raw); - } - return ""; -} - -/** - * Host EIP-7715 grant consent — editable period fields when adjustment allowed. - */ -export function GrantExecutionPermissionModal({ - request, - onResolve, - onReject, -}: { - request: IGrantExecutionPermissionRequest; - onResolve: (result: IGrantExecutionPermissionResult) => void; - onReject: (error: unknown) => void; -}) { - const { style } = useStyle(); - const copy = style.copy.grantExecutionPermission; - const { account } = style.copy; - const { listTrackedAssets, resolveChain, getKnownAsset } = useWallet(); - const permission = request.request.permission; - const adjustable = permission.isAdjustmentAllowed !== false; - const initialToken = readTokenAddress(permission.data); - - const [tokenOptions, setTokenOptions] = useState< - Array<{ address: string; symbol: string; decimals: number; label: string }> - >([]); - const [tokenAddress, setTokenAddress] = useState(initialToken ?? ""); - const [amountText, setAmountText] = useState(""); - const [durationText, setDurationText] = useState( - readDuration(permission.data), - ); - const [startText, setStartText] = useState(readStart(permission.data)); - const [memo, setMemo] = useState(() => readInitialMemo(permission.data)); - const userEditedAmount = useRef(false); - - useEffect(() => { - let cancelled = false; - void listTrackedAssets().then(async (assets) => { - if (cancelled) return; - const onChain = assets.filter( - (a) => - a.type === EAssetType.Erc20 && - String(a.chainId).toLowerCase() === - String(request.request.chainId).toLowerCase(), - ); - const options = onChain.map((a) => ({ - address: getAddress(String(a.address)), - symbol: a.symbol, - decimals: a.decimals ?? 6, - label: `${a.symbol} (${a.name})`, - })); - if ( - initialToken && - !options.some( - (o) => o.address.toLowerCase() === initialToken.toLowerCase(), - ) - ) { - try { - const known = await getKnownAsset( - request.request.chainId, - EVMAccountAddress(getAddress(initialToken as `0x${string}`)), - ); - options.unshift({ - address: getAddress(initialToken as `0x${string}`), - symbol: known?.symbol ?? "TOKEN", - decimals: known?.decimals ?? 6, - label: known ? `${known.symbol} (${known.name})` : initialToken, - }); - } catch { - // leave options as-is - } - } - if (cancelled) return; - setTokenOptions(options); - if (!tokenAddress && options[0]) { - setTokenAddress(options[0].address); - } - }); - return () => { - cancelled = true; - }; - }, [ - initialToken, - getKnownAsset, - listTrackedAssets, - request.request.chainId, - request.request.to, - tokenAddress, - ]); - - const selected = useMemo(() => { - const match = tokenOptions.find( - (o) => o.address.toLowerCase() === tokenAddress.toLowerCase(), - ); - return match ?? { address: tokenAddress, symbol: "TOKEN", decimals: 6, label: tokenAddress }; - }, [tokenAddress, tokenOptions]); - - useEffect(() => { - userEditedAmount.current = false; - setMemo(readInitialMemo(permission.data)); - setDurationText(readDuration(permission.data)); - setStartText(readStart(permission.data)); - setTokenAddress(initialToken ?? ""); - }, [ - initialToken, - permission.data, - request.request.chainId, - request.request.to, - ]); - - useEffect(() => { - if (!tokenAddress || userEditedAmount.current) return; - const atoms = readAmountAtoms(permission.data); - if (atoms === null) return; - try { - setAmountText(formatUnits(atoms, selected.decimals)); - } catch { - setAmountText(""); - } - }, [ - permission.data, - request.request.chainId, - request.request.to, - selected.decimals, - tokenAddress, - ]); - - const amountError = useMemo(() => { - const trimmed = amountText.trim(); - if (!trimmed) return null; - try { - const parsed = parseUnits(trimmed, selected.decimals); - if (parsed <= 0n) return copy.invalidAmountError; - return null; - } catch { - return copy.invalidAmountError; - } - }, [amountText, copy.invalidAmountError, selected.decimals]); - - const durationError = useMemo(() => { - const trimmed = durationText.trim(); - if (!trimmed) return null; - const n = Number(trimmed); - if (!Number.isFinite(n) || n < 1 || !Number.isInteger(n)) { - return copy.invalidDurationError; - } - return null; - }, [copy.invalidDurationError, durationText]); - - const formReady = - Boolean(tokenAddress) && - amountText.trim() !== "" && - amountError === null && - durationText.trim() !== "" && - durationError === null; - - const chainLabel = - resolveChain(request.request.chainId)?.label ?? request.chainName; - - const body = copy.body - .replace("{domain}", request.domain) - .replace("{to}", request.request.to) - .replace("{chainName}", chainLabel) - .replace("{permissionType}", permission.type); - - const reject = () => { - onReject(new OwsUserRejectedError("User rejected the permission request")); - }; - - const grant = () => { - if (!formReady) return; - const periodAmount = parseUnits(amountText.trim(), selected.decimals); - const periodDuration = Number(durationText.trim()); - const startTrimmed = startText.trim(); - const data: Record = { - tokenAddress: EVMAccountAddress( - getAddress(tokenAddress as `0x${string}`), - ), - periodAmount: `0x${periodAmount.toString(16)}`, - periodDuration, - }; - if (startTrimmed) { - data.startDate = Number(startTrimmed); - } - const nextPermission: IExecutionPermission = { - type: ERC20_TOKEN_PERIODIC, - isAdjustmentAllowed: permission.isAdjustmentAllowed, - data, - }; - onResolve({ permission: nextPermission, memo: memo.trim() }); - }; - - return ( - -

{body}

-
-
-
- {copy.hostLabel} -
-
{request.domain}
-
-
-
- {copy.toLabel} -
-
- -
-
-
-
- {copy.chainLabel} -
-
{chainLabel}
-
-
-
- {copy.permissionTypeLabel} -
-
- {permission.type} -
-
-
- -
-
- - {adjustable && tokenOptions.length > 0 ? ( - - ) : ( -

{tokenAddress || "—"}

- )} -
- - { - userEditedAmount.current = true; - setAmountText(value); - }} - disabled={!adjustable} - error={amountError} - /> - -
- - setDurationText(event.target.value)} - aria-invalid={Boolean(durationError)} - /> -

- {copy.periodDurationHint} -

- {durationError ? ( -

- {durationError} -

- ) : null} -
- -
- - setStartText(event.target.value)} - /> -
- -
- -