├── .changeset ├── README.md └── config.json ├── .github ├── CODEOWNERS └── workflows │ ├── eslint.yml │ ├── publish.yml │ ├── release-web-component.yml │ ├── sync-staging.yml │ ├── tests.yml │ └── widget-tests.yml ├── .gitignore ├── .gitmodules ├── .vscode └── settings.json ├── .yarn ├── plugins │ └── @yarnpkg │ │ ├── plugin-typescript.cjs │ │ └── plugin-workspace-tools.cjs └── releases │ └── yarn-3.2.3.cjs ├── .yarnrc.yml ├── AGENTS.md ├── LICENSE.txt ├── README.md ├── RELEASE_PROCESS.md ├── docs ├── advanced-swapping │ ├── allow_unsafe-preventing-handling-bad-execution.mdx │ ├── safe-swapping-how-to-protect-users-from-harming-themselves.mdx │ ├── smart-swap-options.mdx │ └── understanding-quote-quality-metrics.mdx ├── advanced-transfer │ ├── cw20-swaps.mdx │ ├── evm-transactions.mdx │ ├── experimental-features.mdx │ ├── go-fast.mdx │ ├── handling-cross-chain-failure-cases.mdx │ ├── ibc-routing-algorithm.mdx │ ├── interpreting-transaction-status.mdx │ └── svm-transaction-details.mdx ├── anything-anywhere │ └── how-to-generate-shareable-links.mdx ├── api-reference │ ├── error-codes.mdx │ └── prod │ │ ├── fungible │ │ ├── get-v2fungibleassets.mdx │ │ ├── get-v2fungiblevenues.mdx │ │ ├── post-v2fungibleassets_between_chains.mdx │ │ ├── post-v2fungibleassets_from_source.mdx │ │ ├── post-v2fungibleibc_origin_assets.mdx │ │ ├── post-v2fungiblemsgs.mdx │ │ ├── post-v2fungiblemsgs_direct.mdx │ │ └── post-v2fungibleroute.mdx │ │ ├── info │ │ ├── get-v2infobridges.mdx │ │ ├── get-v2infochains.mdx │ │ └── post-v2infobalances.mdx │ │ └── transaction │ │ ├── get-v2txstatus.mdx │ │ ├── post-v2txsubmit.mdx │ │ └── post-v2txtrack.mdx ├── client │ ├── advanced-features.mdx │ ├── balance-gas-and-fee-tooling.mdx │ ├── executing-a-route.mdx │ ├── getting-started.mdx │ └── migration-guide.mdx ├── docs.json ├── eureka │ ├── contract-addresses.mdx │ ├── custom-erc20-integration.mdx │ ├── eureka-overview.mdx │ ├── eureka-tech-overview.mdx │ ├── integration-guide.mdx │ └── security-properties.mdx ├── favicon.ico ├── favicon.png ├── general │ ├── affiliate-fees.mdx │ ├── api-keys.mdx │ ├── faq.mdx │ ├── fee-info.mdx │ ├── getting-started.mdx │ ├── multi-chain-realtime-transaction-and-packet-tracking.mdx │ ├── overview-and-typical-usage.mdx │ ├── post-route-actions.mdx │ ├── quickstart-guide.mdx │ ├── smart-relay.mdx │ ├── supported-ecosystems-and-bridges.mdx │ └── upcoming-features.mdx ├── images │ ├── 12bf3f9-Screen_Shot_2023-07-25_at_3.26.30_PM.png │ ├── 2.png │ ├── 2477aba-Screen_Shot_2023-07-25_at_3.48.15_PM.png │ ├── 3dde446-case3.jpg │ ├── 3ff1176-Screen_Shot_2023-07-25_at_11.46.38_AM.png │ ├── 443a393-Screen_Shot_2023-07-25_at_11.46.47_AM.png │ ├── 51dc78a-Screen_Shot_2023-07-25_at_3.04.14_PM.png │ ├── 568550d-case2.jpg │ ├── 5b993ce-swap-warning-image.png │ ├── 5c93590-error-type-2.png │ ├── 66a9d2b-Screen_Shot_2023-07-25_at_9.50.00_AM.png │ ├── 6744b96-error-1-diagram.png │ ├── 78d8d3f-CleanShot_2024-05-23_at_15.33.182x.png │ ├── 9afddc0-case1.jpg │ ├── a61a075-swap-warning-page-image.png │ ├── a83beaf-CleanShot_2024-05-23_at_15.59.452x.png │ ├── b42b522-CleanShot_2024-05-23_at_15.38.522x.png │ ├── ca61685-CleanShot_2024-05-23_at_15.28.242x.png │ ├── cc4fe76-Screenshot_2024-03-07_at_9.12.25_AM.png │ ├── eb72804-Screen_Shot_2023-07-25_at_3.59.53_PM.png │ ├── go-banner.svg │ └── shareable-links.png ├── legal-and-privacy │ ├── privacy-policy.mdx │ └── terms-of-service.mdx ├── logo │ ├── dark.svg │ └── light.svg ├── release-notes │ ├── api.mdx │ ├── client.mdx │ ├── overview.mdx │ └── widget.mdx ├── style.css ├── support-requirements │ ├── chain-support-requirements.mdx │ ├── swap-venue-requirements.mdx │ └── token-support-requirements.mdx ├── swagger.yml └── widget │ ├── configuration.mdx │ ├── connected-wallet.mdx │ ├── faq.mdx │ ├── getting-started.mdx │ ├── migration-guide.mdx │ └── web-component.mdx ├── examples ├── client │ ├── README.md │ ├── index.ts │ ├── package.json │ ├── stats.html │ └── vite.config.mjs ├── nextjs │ ├── .gitignore │ ├── CHANGELOG.md │ ├── README.md │ ├── next.config.ts │ ├── package.json │ ├── public │ │ ├── gobg-dark.svg │ │ └── gobg-light.svg │ ├── src │ │ ├── app │ │ │ ├── globals.css │ │ │ ├── injected │ │ │ │ └── page.tsx │ │ │ ├── layout.tsx │ │ │ └── page.tsx │ │ └── hooks │ │ │ └── useURLQueryParams.tsx │ ├── tsconfig.json │ └── window.d.ts ├── nuxtjs │ ├── .gitignore │ ├── README.md │ ├── app.vue │ ├── nuxt.config.ts │ ├── package.json │ ├── plugins │ │ └── skip-widget.client.ts │ ├── public │ │ ├── favicon.ico │ │ └── robots.txt │ ├── server │ │ └── tsconfig.json │ └── tsconfig.json └── raw-html.html ├── local_vendor ├── OPinit │ └── proto │ │ └── opinit │ │ └── ophost │ │ └── v1 │ │ ├── tx.proto │ │ └── types.proto └── initia │ └── proto │ └── initia │ └── move │ └── v1 │ └── tx.proto ├── package.json ├── packages ├── client │ ├── .eslintrc.cjs │ ├── CHANGELOG.md │ ├── Makefile │ ├── README.md │ ├── e2e │ │ └── utils.ts │ ├── generateTypes.mjs │ ├── jest.config.cjs │ ├── package.json │ ├── scripts │ │ ├── codegen.cjs │ │ ├── dev-setup.sh │ │ ├── port-forward.sh │ │ └── prepublish.cjs │ ├── src │ │ ├── __test__ │ │ │ ├── client.test.ts │ │ │ └── convert.test.ts │ │ ├── amino │ │ │ ├── README.md │ │ │ ├── addresses.ts │ │ │ ├── encoding.ts │ │ │ ├── pubkey.ts │ │ │ └── signature.ts │ │ ├── api │ │ │ ├── getAssets.ts │ │ │ ├── getAssetsBetweenChains.ts │ │ │ ├── getBridges.ts │ │ │ ├── getChains.ts │ │ │ ├── getVenues.ts │ │ │ ├── postAssetsFromSource.ts │ │ │ ├── postBalances.ts │ │ │ ├── postIbcOriginAssets.ts │ │ │ ├── postMessages.ts │ │ │ ├── postMessagesDirect.ts │ │ │ ├── postRecommendAssets.ts │ │ │ ├── postRoute.ts │ │ │ ├── postSubmit.ts │ │ │ ├── postSubmitTransaction.ts │ │ │ ├── postTrackTransaction.ts │ │ │ └── postTransactionStatus.ts │ │ ├── chains.ts │ │ ├── codegen │ │ │ ├── .gitkeep │ │ │ ├── amino │ │ │ │ └── amino.ts │ │ │ ├── chains.json │ │ │ ├── circle │ │ │ │ ├── cctp │ │ │ │ │ └── v1 │ │ │ │ │ │ ├── attester.ts │ │ │ │ │ │ ├── burn_message.ts │ │ │ │ │ │ ├── burning_and_minting_paused.ts │ │ │ │ │ │ ├── events.ts │ │ │ │ │ │ ├── genesis.ts │ │ │ │ │ │ ├── max_message_body_size.ts │ │ │ │ │ │ ├── message.ts │ │ │ │ │ │ ├── nonce.ts │ │ │ │ │ │ ├── per_message_burn_limit.ts │ │ │ │ │ │ ├── query.ts │ │ │ │ │ │ ├── remote_token_messenger.ts │ │ │ │ │ │ ├── sending_and_receiving_messages_paused.ts │ │ │ │ │ │ ├── signature_threshold.ts │ │ │ │ │ │ ├── token_pair.ts │ │ │ │ │ │ ├── tx.amino.ts │ │ │ │ │ │ ├── tx.registry.ts │ │ │ │ │ │ └── tx.ts │ │ │ │ └── client.ts │ │ │ ├── cosmos │ │ │ │ ├── app │ │ │ │ │ ├── runtime │ │ │ │ │ │ └── v1alpha1 │ │ │ │ │ │ │ └── module.ts │ │ │ │ │ └── v1alpha1 │ │ │ │ │ │ ├── config.ts │ │ │ │ │ │ ├── module.ts │ │ │ │ │ │ └── query.ts │ │ │ │ ├── auth │ │ │ │ │ └── v1beta1 │ │ │ │ │ │ ├── auth.ts │ │ │ │ │ │ ├── genesis.ts │ │ │ │ │ │ ├── query.ts │ │ │ │ │ │ ├── tx.amino.ts │ │ │ │ │ │ ├── tx.registry.ts │ │ │ │ │ │ └── tx.ts │ │ │ │ ├── authz │ │ │ │ │ └── v1beta1 │ │ │ │ │ │ ├── authz.ts │ │ │ │ │ │ ├── event.ts │ │ │ │ │ │ ├── genesis.ts │ │ │ │ │ │ ├── query.ts │ │ │ │ │ │ ├── tx.amino.ts │ │ │ │ │ │ ├── tx.registry.ts │ │ │ │ │ │ └── tx.ts │ │ │ │ ├── autocli │ │ │ │ │ └── v1 │ │ │ │ │ │ ├── options.ts │ │ │ │ │ │ └── query.ts │ │ │ │ ├── bank │ │ │ │ │ └── v1beta1 │ │ │ │ │ │ ├── authz.ts │ │ │ │ │ │ ├── bank.ts │ │ │ │ │ │ ├── genesis.ts │ │ │ │ │ │ ├── query.ts │ │ │ │ │ │ ├── tx.amino.ts │ │ │ │ │ │ ├── tx.registry.ts │ │ │ │ │ │ └── tx.ts │ │ │ │ ├── base │ │ │ │ │ ├── abci │ │ │ │ │ │ └── v1beta1 │ │ │ │ │ │ │ └── abci.ts │ │ │ │ │ ├── kv │ │ │ │ │ │ └── v1beta1 │ │ │ │ │ │ │ └── kv.ts │ │ │ │ │ ├── node │ │ │ │ │ │ └── v1beta1 │ │ │ │ │ │ │ └── query.ts │ │ │ │ │ ├── query │ │ │ │ │ │ └── v1beta1 │ │ │ │ │ │ │ └── pagination.ts │ │ │ │ │ ├── reflection │ │ │ │ │ │ ├── v1beta1 │ │ │ │ │ │ │ └── reflection.ts │ │ │ │ │ │ └── v2alpha1 │ │ │ │ │ │ │ └── reflection.ts │ │ │ │ │ ├── snapshots │ │ │ │ │ │ └── v1beta1 │ │ │ │ │ │ │ └── snapshot.ts │ │ │ │ │ ├── store │ │ │ │ │ │ └── v1beta1 │ │ │ │ │ │ │ ├── commit_info.ts │ │ │ │ │ │ │ └── listening.ts │ │ │ │ │ ├── tendermint │ │ │ │ │ │ └── v1beta1 │ │ │ │ │ │ │ ├── query.ts │ │ │ │ │ │ │ └── types.ts │ │ │ │ │ └── v1beta1 │ │ │ │ │ │ └── coin.ts │ │ │ │ ├── capability │ │ │ │ │ └── v1beta1 │ │ │ │ │ │ ├── capability.ts │ │ │ │ │ │ └── genesis.ts │ │ │ │ ├── client.ts │ │ │ │ ├── consensus │ │ │ │ │ └── v1 │ │ │ │ │ │ ├── query.ts │ │ │ │ │ │ ├── tx.amino.ts │ │ │ │ │ │ ├── tx.registry.ts │ │ │ │ │ │ └── tx.ts │ │ │ │ ├── crisis │ │ │ │ │ └── v1beta1 │ │ │ │ │ │ ├── genesis.ts │ │ │ │ │ │ ├── tx.amino.ts │ │ │ │ │ │ ├── tx.registry.ts │ │ │ │ │ │ └── tx.ts │ │ │ │ ├── crypto │ │ │ │ │ ├── ed25519 │ │ │ │ │ │ └── keys.ts │ │ │ │ │ ├── hd │ │ │ │ │ │ └── v1 │ │ │ │ │ │ │ └── hd.ts │ │ │ │ │ ├── keyring │ │ │ │ │ │ └── v1 │ │ │ │ │ │ │ └── record.ts │ │ │ │ │ ├── multisig │ │ │ │ │ │ ├── keys.ts │ │ │ │ │ │ └── v1beta1 │ │ │ │ │ │ │ └── multisig.ts │ │ │ │ │ ├── secp256k1 │ │ │ │ │ │ └── keys.ts │ │ │ │ │ └── secp256r1 │ │ │ │ │ │ └── keys.ts │ │ │ │ ├── distribution │ │ │ │ │ └── v1beta1 │ │ │ │ │ │ ├── distribution.ts │ │ │ │ │ │ ├── genesis.ts │ │ │ │ │ │ ├── query.ts │ │ │ │ │ │ ├── tx.amino.ts │ │ │ │ │ │ ├── tx.registry.ts │ │ │ │ │ │ └── tx.ts │ │ │ │ ├── evidence │ │ │ │ │ └── v1beta1 │ │ │ │ │ │ ├── evidence.ts │ │ │ │ │ │ ├── genesis.ts │ │ │ │ │ │ ├── query.ts │ │ │ │ │ │ ├── tx.amino.ts │ │ │ │ │ │ ├── tx.registry.ts │ │ │ │ │ │ └── tx.ts │ │ │ │ ├── feegrant │ │ │ │ │ └── v1beta1 │ │ │ │ │ │ ├── feegrant.ts │ │ │ │ │ │ ├── genesis.ts │ │ │ │ │ │ ├── query.ts │ │ │ │ │ │ ├── tx.amino.ts │ │ │ │ │ │ ├── tx.registry.ts │ │ │ │ │ │ └── tx.ts │ │ │ │ ├── genutil │ │ │ │ │ └── v1beta1 │ │ │ │ │ │ └── genesis.ts │ │ │ │ ├── gov │ │ │ │ │ ├── v1 │ │ │ │ │ │ ├── genesis.ts │ │ │ │ │ │ ├── gov.ts │ │ │ │ │ │ ├── query.ts │ │ │ │ │ │ ├── tx.amino.ts │ │ │ │ │ │ ├── tx.registry.ts │ │ │ │ │ │ └── tx.ts │ │ │ │ │ └── v1beta1 │ │ │ │ │ │ ├── genesis.ts │ │ │ │ │ │ ├── gov.ts │ │ │ │ │ │ ├── query.ts │ │ │ │ │ │ ├── tx.amino.ts │ │ │ │ │ │ ├── tx.registry.ts │ │ │ │ │ │ └── tx.ts │ │ │ │ ├── group │ │ │ │ │ └── v1 │ │ │ │ │ │ ├── events.ts │ │ │ │ │ │ ├── genesis.ts │ │ │ │ │ │ ├── query.ts │ │ │ │ │ │ ├── tx.amino.ts │ │ │ │ │ │ ├── tx.registry.ts │ │ │ │ │ │ ├── tx.ts │ │ │ │ │ │ └── types.ts │ │ │ │ ├── mint │ │ │ │ │ └── v1beta1 │ │ │ │ │ │ ├── genesis.ts │ │ │ │ │ │ ├── mint.ts │ │ │ │ │ │ ├── query.ts │ │ │ │ │ │ ├── tx.amino.ts │ │ │ │ │ │ ├── tx.registry.ts │ │ │ │ │ │ └── tx.ts │ │ │ │ ├── msg │ │ │ │ │ └── v1 │ │ │ │ │ │ └── msg.ts │ │ │ │ ├── nft │ │ │ │ │ └── v1beta1 │ │ │ │ │ │ ├── event.ts │ │ │ │ │ │ ├── genesis.ts │ │ │ │ │ │ ├── nft.ts │ │ │ │ │ │ ├── query.ts │ │ │ │ │ │ ├── tx.amino.ts │ │ │ │ │ │ ├── tx.registry.ts │ │ │ │ │ │ └── tx.ts │ │ │ │ ├── orm │ │ │ │ │ ├── query │ │ │ │ │ │ └── v1alpha1 │ │ │ │ │ │ │ └── query.ts │ │ │ │ │ ├── v1 │ │ │ │ │ │ └── orm.ts │ │ │ │ │ └── v1alpha1 │ │ │ │ │ │ └── schema.ts │ │ │ │ ├── params │ │ │ │ │ └── v1beta1 │ │ │ │ │ │ ├── params.ts │ │ │ │ │ │ └── query.ts │ │ │ │ ├── query │ │ │ │ │ └── v1 │ │ │ │ │ │ └── query.ts │ │ │ │ ├── reflection │ │ │ │ │ └── v1 │ │ │ │ │ │ └── reflection.ts │ │ │ │ ├── slashing │ │ │ │ │ └── v1beta1 │ │ │ │ │ │ ├── genesis.ts │ │ │ │ │ │ ├── query.ts │ │ │ │ │ │ ├── slashing.ts │ │ │ │ │ │ ├── tx.amino.ts │ │ │ │ │ │ ├── tx.registry.ts │ │ │ │ │ │ └── tx.ts │ │ │ │ ├── staking │ │ │ │ │ └── v1beta1 │ │ │ │ │ │ ├── authz.ts │ │ │ │ │ │ ├── genesis.ts │ │ │ │ │ │ ├── query.ts │ │ │ │ │ │ ├── staking.ts │ │ │ │ │ │ ├── tx.amino.ts │ │ │ │ │ │ ├── tx.registry.ts │ │ │ │ │ │ └── tx.ts │ │ │ │ ├── tx │ │ │ │ │ ├── config │ │ │ │ │ │ └── v1 │ │ │ │ │ │ │ └── config.ts │ │ │ │ │ ├── signing │ │ │ │ │ │ └── v1beta1 │ │ │ │ │ │ │ └── signing.ts │ │ │ │ │ └── v1beta1 │ │ │ │ │ │ ├── service.ts │ │ │ │ │ │ └── tx.ts │ │ │ │ ├── upgrade │ │ │ │ │ └── v1beta1 │ │ │ │ │ │ ├── query.ts │ │ │ │ │ │ ├── tx.amino.ts │ │ │ │ │ │ ├── tx.registry.ts │ │ │ │ │ │ ├── tx.ts │ │ │ │ │ │ └── upgrade.ts │ │ │ │ └── vesting │ │ │ │ │ └── v1beta1 │ │ │ │ │ ├── tx.amino.ts │ │ │ │ │ ├── tx.registry.ts │ │ │ │ │ ├── tx.ts │ │ │ │ │ └── vesting.ts │ │ │ ├── cosmos_proto │ │ │ │ └── cosmos.ts │ │ │ ├── ethermint │ │ │ │ ├── client.ts │ │ │ │ ├── crypto │ │ │ │ │ └── v1 │ │ │ │ │ │ └── ethsecp256k1 │ │ │ │ │ │ └── keys.ts │ │ │ │ ├── evm │ │ │ │ │ └── v1 │ │ │ │ │ │ ├── events.ts │ │ │ │ │ │ ├── evm.ts │ │ │ │ │ │ ├── genesis.ts │ │ │ │ │ │ ├── query.ts │ │ │ │ │ │ ├── tx.amino.ts │ │ │ │ │ │ ├── tx.registry.ts │ │ │ │ │ │ └── tx.ts │ │ │ │ ├── feemarket │ │ │ │ │ └── v1 │ │ │ │ │ │ ├── events.ts │ │ │ │ │ │ ├── feemarket.ts │ │ │ │ │ │ ├── genesis.ts │ │ │ │ │ │ ├── query.ts │ │ │ │ │ │ ├── tx.amino.ts │ │ │ │ │ │ ├── tx.registry.ts │ │ │ │ │ │ └── tx.ts │ │ │ │ └── types │ │ │ │ │ └── v1 │ │ │ │ │ ├── account.ts │ │ │ │ │ ├── dynamic_fee.ts │ │ │ │ │ ├── indexer.ts │ │ │ │ │ └── web3.ts │ │ │ ├── evmos │ │ │ │ ├── client.ts │ │ │ │ ├── epochs │ │ │ │ │ └── v1 │ │ │ │ │ │ ├── genesis.ts │ │ │ │ │ │ └── query.ts │ │ │ │ ├── erc20 │ │ │ │ │ └── v1 │ │ │ │ │ │ ├── erc20.ts │ │ │ │ │ │ ├── events.ts │ │ │ │ │ │ ├── genesis.ts │ │ │ │ │ │ ├── query.ts │ │ │ │ │ │ ├── tx.amino.ts │ │ │ │ │ │ ├── tx.registry.ts │ │ │ │ │ │ └── tx.ts │ │ │ │ ├── incentives │ │ │ │ │ └── v1 │ │ │ │ │ │ ├── genesis.ts │ │ │ │ │ │ └── incentives.ts │ │ │ │ ├── inflation │ │ │ │ │ └── v1 │ │ │ │ │ │ ├── genesis.ts │ │ │ │ │ │ ├── inflation.ts │ │ │ │ │ │ ├── query.ts │ │ │ │ │ │ ├── tx.amino.ts │ │ │ │ │ │ ├── tx.registry.ts │ │ │ │ │ │ └── tx.ts │ │ │ │ ├── revenue │ │ │ │ │ └── v1 │ │ │ │ │ │ ├── events.ts │ │ │ │ │ │ ├── genesis.ts │ │ │ │ │ │ ├── query.ts │ │ │ │ │ │ ├── revenue.ts │ │ │ │ │ │ ├── tx.amino.ts │ │ │ │ │ │ ├── tx.registry.ts │ │ │ │ │ │ └── tx.ts │ │ │ │ └── vesting │ │ │ │ │ ├── v1 │ │ │ │ │ └── vesting.ts │ │ │ │ │ └── v2 │ │ │ │ │ ├── events.ts │ │ │ │ │ ├── query.ts │ │ │ │ │ ├── tx.amino.ts │ │ │ │ │ ├── tx.registry.ts │ │ │ │ │ ├── tx.ts │ │ │ │ │ └── vesting.ts │ │ │ ├── gogoproto │ │ │ │ └── gogo.ts │ │ │ ├── google │ │ │ │ ├── api │ │ │ │ │ ├── annotations.ts │ │ │ │ │ └── http.ts │ │ │ │ └── protobuf │ │ │ │ │ ├── any.ts │ │ │ │ │ ├── descriptor.ts │ │ │ │ │ ├── duration.ts │ │ │ │ │ ├── empty.ts │ │ │ │ │ └── timestamp.ts │ │ │ ├── helpers.ts │ │ │ ├── initia │ │ │ │ ├── client.ts │ │ │ │ └── move │ │ │ │ │ └── v1 │ │ │ │ │ ├── tx.amino.ts │ │ │ │ │ ├── tx.registry.ts │ │ │ │ │ └── tx.ts │ │ │ ├── json-safe.ts │ │ │ ├── opinit │ │ │ │ ├── client.ts │ │ │ │ └── ophost │ │ │ │ │ └── v1 │ │ │ │ │ ├── tx.amino.ts │ │ │ │ │ ├── tx.registry.ts │ │ │ │ │ ├── tx.ts │ │ │ │ │ └── types.ts │ │ │ └── tendermint │ │ │ │ ├── abci │ │ │ │ └── types.ts │ │ │ │ ├── crypto │ │ │ │ ├── keys.ts │ │ │ │ └── proof.ts │ │ │ │ ├── libs │ │ │ │ └── bits │ │ │ │ │ └── types.ts │ │ │ │ ├── p2p │ │ │ │ └── types.ts │ │ │ │ ├── types │ │ │ │ ├── block.ts │ │ │ │ ├── evidence.ts │ │ │ │ ├── params.ts │ │ │ │ ├── types.ts │ │ │ │ └── validator.ts │ │ │ │ └── version │ │ │ │ └── types.ts │ │ ├── constants │ │ │ ├── abis.ts │ │ │ └── constants.ts │ │ ├── index.ts │ │ ├── injective │ │ │ └── index.ts │ │ ├── private-functions │ │ │ ├── cosmos │ │ │ │ ├── executeCosmosTransaction.ts │ │ │ │ ├── getEncodeObjectFromCosmosMessage.ts │ │ │ │ ├── signCosmosMessageAmino.ts │ │ │ │ ├── signCosmosMessageDirect.ts │ │ │ │ ├── signCosmosMessageDirectEvmos.ts │ │ │ │ ├── signCosmosMessageDirectInjective.ts │ │ │ │ ├── signCosmosTransaction.ts │ │ │ │ └── validateCosmosGasBalance.ts │ │ │ ├── evm │ │ │ │ ├── executeEvmTransaction.ts │ │ │ │ ├── validateEvmGasBalance.ts │ │ │ │ └── validateEvmTokenApproval.ts │ │ │ ├── executeTransactions.ts │ │ │ ├── getAccountNumberAndSequence.ts │ │ │ ├── getDefaultGasTokenForChain.ts │ │ │ ├── getMainnetAndTestnetAssets.ts │ │ │ ├── getMainnetAndTestnetChains.ts │ │ │ ├── getRestEndpointForChain.ts │ │ │ ├── getRpcEndpointForChain.ts │ │ │ ├── svm │ │ │ │ ├── executeSvmTransaction.ts │ │ │ │ ├── signSvmTransaction.ts │ │ │ │ └── validateSvmGasBalance.ts │ │ │ └── validateGasBalances.ts │ │ ├── proto-signing │ │ │ ├── README.md │ │ │ ├── pubkey.ts │ │ │ └── signer.ts │ │ ├── public-functions │ │ │ ├── executeRoute.ts │ │ │ ├── getCosmosGasAmountForMessage.ts │ │ │ ├── getEvmGasAmountForMessage.ts │ │ │ ├── getFeeInfoForChain.ts │ │ │ ├── getRecommendedGasPrice.ts │ │ │ ├── getSigningStargateClient.ts │ │ │ ├── setApiOptions.ts │ │ │ ├── setClientOptions.ts │ │ │ └── waitForTransaction.ts │ │ ├── registry.ts │ │ ├── state │ │ │ ├── apiState.ts │ │ │ └── clientState.ts │ │ ├── stride │ │ │ └── index.ts │ │ ├── types │ │ │ ├── callbacks.ts │ │ │ ├── client-types.ts │ │ │ ├── index.ts │ │ │ ├── swaggerTypes.ts │ │ │ └── swaggerTypesJson.ts │ │ └── utils │ │ │ ├── convert.ts │ │ │ ├── filterMessagesRequest.ts │ │ │ ├── generateApi.ts │ │ │ └── timer.ts │ ├── starship │ │ ├── ci.yaml │ │ └── local.yaml │ ├── tsconfig.build.json │ ├── tsconfig.json │ ├── tsup.config.ts │ ├── vitest.config.mjs │ ├── vitest.e2e.config.mjs │ └── vitest.unit.config.mjs └── widget │ ├── .env.example │ ├── .storybook │ ├── main.ts │ └── preview.tsx │ ├── CHANGELOG.md │ ├── README.md │ ├── __tests__ │ ├── Keplr.test.tsx │ ├── Widget │ │ ├── both-assets-selected.png │ │ ├── connect-keplr.png │ │ ├── default-widget.png │ │ └── usdc-noble-selected.png │ └── setup │ │ ├── globalSetup.ts │ │ ├── helpers.ts │ │ ├── keplr.ts │ │ ├── playwright.ts │ │ ├── types.ts │ │ └── utils.ts │ ├── eslint.config.mjs │ ├── index.html │ ├── package.json │ ├── playwright.config.ts │ ├── scripts │ ├── generate-chains.cjs │ └── prepublish.cjs │ ├── setupTests.ts │ ├── src │ ├── components │ │ ├── Button.tsx │ │ ├── EvmDisclaimer.tsx │ │ ├── GoFastSymbol.tsx │ │ ├── GroupedAssetImage.tsx │ │ ├── Layout.tsx │ │ ├── MainButton.tsx │ │ ├── Modal.tsx │ │ ├── ModalHeader.tsx │ │ ├── ModalRowItem.tsx │ │ ├── PageHeader.tsx │ │ ├── RenderWalletList.tsx │ │ ├── Skeleton.tsx │ │ ├── Tooltip.tsx │ │ ├── Typography.tsx │ │ └── VirtualList.tsx │ ├── constants │ │ ├── abis.ts │ │ ├── chains.ts │ │ ├── cosmosChains │ │ │ ├── explorers.json │ │ │ ├── mainnet.json │ │ │ └── testnet.json │ │ ├── graz.ts │ │ ├── skipClientDefault.ts │ │ ├── solana.ts │ │ ├── wagmi.ts │ │ └── widget.ts │ ├── devMode │ │ ├── global.css │ │ └── loadWidget.tsx │ ├── fonts │ │ ├── ABCDiatype-Bold.woff2 │ │ ├── ABCDiatype-Medium.woff2 │ │ ├── ABCDiatype-Regular copy.woff2 │ │ ├── ABCDiatype-Regular.woff2 │ │ └── ABCDiatypeMono-Medium.woff2 │ ├── hooks │ │ ├── useAutoSetAddress.ts │ │ ├── useCW20Balance.ts │ │ ├── useCopyAddress.ts │ │ ├── useCosmosFeeAssetValidation.ts │ │ ├── useCreateCosmosWallets.tsx │ │ ├── useCreateEvmWallets.tsx │ │ ├── useCreateSolanaWallets.tsx │ │ ├── useFetchAllBalances.ts │ │ ├── useGetAccount.ts │ │ ├── useGetAssetDetails.tsx │ │ ├── useGetBalance.ts │ │ ├── useGetSourceBalance.ts │ │ ├── useInjectFontsToDocumentHead.ts │ │ ├── useIsGoFast.ts │ │ ├── useIsMobileScreenSize.ts │ │ ├── useKeepWalletStateSynced.ts │ │ ├── useMobileRouteConfig.ts │ │ ├── usePreventPageUnload.ts │ │ ├── usePrimaryChainIdForChainType.ts │ │ ├── useSettingsChanged.ts │ │ ├── useSettingsDrawer.tsx │ │ ├── useShowCosmosLedgerWarning.ts │ │ ├── useSwitchEvmChain.tsx │ │ ├── useTxHistory.ts │ │ ├── useUpdateSourceAssetToDefaultForChainType.ts │ │ └── useWalletList.tsx │ ├── icons │ │ ├── ArrowIcon.tsx │ │ ├── BridgeArrowIcon.tsx │ │ ├── BridgeIcon.tsx │ │ ├── ChainIcon.tsx │ │ ├── CheckmarkIcon.tsx │ │ ├── ChevronIcon.tsx │ │ ├── CogIcon.tsx │ │ ├── CopyIcon.tsx │ │ ├── FilledWarningIcon.tsx │ │ ├── GasIcon.tsx │ │ ├── GoFastIcon.tsx │ │ ├── HamburgerIcon.tsx │ │ ├── HistoryArrowIcon.tsx │ │ ├── HistoryIcon.tsx │ │ ├── HorizontalLineIcon.tsx │ │ ├── LightningIcon.tsx │ │ ├── PenIcon.tsx │ │ ├── PlusIcon.tsx │ │ ├── QuestionMarkIcon.tsx │ │ ├── RouteArrow.tsx │ │ ├── SearchIcon.tsx │ │ ├── SignatureIcon.tsx │ │ ├── SkipLogoIcon.tsx │ │ ├── SpeedometerIcon.tsx │ │ ├── SpinnerIcon.tsx │ │ ├── SwapExecutionBridgeIcon.tsx │ │ ├── SwapExecutionSendIcon.tsx │ │ ├── SwapExecutionSwapIcon.tsx │ │ ├── SwapIcon.tsx │ │ ├── ThinArrowIcon.tsx │ │ ├── TinyTriangleIcon.tsx │ │ ├── TrashIcon.tsx │ │ ├── TriangleWarningIcon.tsx │ │ ├── WarningIcon.tsx │ │ ├── XIcon.tsx │ │ └── index.tsx │ ├── index.tsx │ ├── modals │ │ ├── AssetAndChainSelectorModal │ │ │ ├── AssetAndChainSelectorModal.tsx │ │ │ ├── AssetAndChainSelectorModalRowItem.tsx │ │ │ ├── AssetAndChainSelectorModalSearchInput.tsx │ │ │ ├── useFilteredAssets.ts │ │ │ ├── useFilteredChains.ts │ │ │ └── useGroupedAssetsByRecommendedSymbol.ts │ │ ├── ConnectedWalletModal │ │ │ ├── ConnectEcoRow.tsx │ │ │ ├── ConnectedWalletModal.tsx │ │ │ └── EcosystemConnectors.tsx │ │ ├── SetAddressModal │ │ │ ├── SetAddressModal.tsx │ │ │ └── isValidWalletAddress.ts │ │ ├── SwapSettingsDrawer │ │ │ └── SwapSettingsDrawer.tsx │ │ ├── WalletSelectorModal │ │ │ └── WalletSelectorModal.tsx │ │ └── registerModals.ts │ ├── pages │ │ ├── ErrorWarningPage │ │ │ ├── ErrorWarningPage.tsx │ │ │ ├── ErrorWarningPageContent.tsx │ │ │ ├── ExpectedErrorPage │ │ │ │ ├── ExpectedErrorPageAuthFailed.tsx │ │ │ │ ├── ExpectedErrorPageInsufficientGasBalance.tsx │ │ │ │ └── ExpectedErrorPageRelayFeeQuoteExpired.tsx │ │ │ ├── UnexpectedErrorPage │ │ │ │ ├── UnexpectedErrorPageTimeout.tsx │ │ │ │ ├── UnexpectedErrorPageTransactionFailed.tsx │ │ │ │ ├── UnexpectedErrorPageTransactionReverted.tsx │ │ │ │ └── UnexpectedErrorPageUnexpected.tsx │ │ │ └── WarningPage │ │ │ │ ├── WarningPageBadPrice.tsx │ │ │ │ ├── WarningPageCosmosLedger.tsx │ │ │ │ ├── WarningPageGoFast.tsx │ │ │ │ ├── WarningPageLowInfo.tsx │ │ │ │ └── WarningPageTradeAdditionalSigningRequired.tsx │ │ ├── SwapExecutionPage │ │ │ ├── SwapExecutionButton.tsx │ │ │ ├── SwapExecutionPage.tsx │ │ │ ├── SwapExecutionPageRouteDetailed.tsx │ │ │ ├── SwapExecutionPageRouteDetailedRow.tsx │ │ │ ├── SwapExecutionPageRouteSimple.tsx │ │ │ ├── SwapExecutionPageRouteSimpleRow.tsx │ │ │ ├── useBroadcastedTxs.ts │ │ │ ├── useCountdown.ts │ │ │ ├── useHandleTransactionFailed.tsx │ │ │ ├── useHandleTransactionTimeout.tsx │ │ │ ├── useIsGasStationTx.tsx │ │ │ ├── useSwapExecutionState.ts │ │ │ └── useSyncTxStatus.tsx │ │ ├── SwapPage │ │ │ ├── ConnectedWalletContent.tsx │ │ │ ├── RoutePreferenceSelector.tsx │ │ │ ├── SlippageSelector.tsx │ │ │ ├── SwapPage.tsx │ │ │ ├── SwapPageAssetChainInput.tsx │ │ │ ├── SwapPageBridge.tsx │ │ │ ├── SwapPageFooter.tsx │ │ │ ├── SwapPageHeader.tsx │ │ │ ├── useCleanupDebouncedAtoms.ts │ │ │ ├── useConnectToMissingCosmosChain.ts │ │ │ ├── useSetMaxAmount.ts │ │ │ └── useUpdateAmountWhenRouteChanges.ts │ │ └── TransactionHistoryPage │ │ │ ├── TransactionHistoryPage.tsx │ │ │ ├── TransactionHistoryPageHistoryItem.tsx │ │ │ └── TransactionHistoryPageHistoryItemDetails.tsx │ ├── providers │ │ ├── CosmosProvider.tsx │ │ └── EVMProvider.tsx │ ├── state │ │ ├── assetSymbolsSortedToTop.ts │ │ ├── balances.ts │ │ ├── callbacks.ts │ │ ├── errorWarning.ts │ │ ├── filters.ts │ │ ├── hideAssetsUnlessWalletTypeConnected.ts │ │ ├── history.ts │ │ ├── ibcEurekaHighlightedAssets.ts │ │ ├── localStorageKeys.ts │ │ ├── route.ts │ │ ├── router.ts │ │ ├── settingsDrawer.ts │ │ ├── skipClient.ts │ │ ├── swapExecutionPage.ts │ │ ├── swapPage.ts │ │ ├── types.ts │ │ └── wallets.ts │ ├── stories │ │ ├── AssetChainInput.stories.tsx │ │ ├── ErrorPage.stories.tsx │ │ ├── ErrorState.stories.tsx │ │ ├── GhostButton.stories.tsx │ │ ├── MainButton.stories.tsx │ │ ├── Modal.stories.tsx │ │ ├── ModalRowItem.stories.tsx │ │ ├── SetAddressModals.stories.tsx │ │ ├── SwapExecutionPage.stories.tsx │ │ ├── SwapPage.stories.tsx │ │ ├── TokenAndChainSelectorModal.stories.tsx │ │ ├── VirtualList.stories.tsx │ │ ├── WalletSelectorModal.stories.tsx │ │ ├── Widget.stories.tsx │ │ ├── exampleOperations.json │ │ ├── renderLightAndDarkTheme.tsx │ │ └── tx_history.json │ ├── utils │ │ ├── atomWithDebounce.ts │ │ ├── bech32.ts │ │ ├── clientType.test.ts │ │ ├── clientType.ts │ │ ├── colors.ts │ │ ├── countdownTimer.ts │ │ ├── crypto.test.ts │ │ ├── crypto.ts │ │ ├── date.test.ts │ │ ├── date.ts │ │ ├── error.ts │ │ ├── explorerLink.ts │ │ ├── intl.ts │ │ ├── migrateOldLocalStorageValues.ts │ │ ├── misc.tsx │ │ ├── number.test.ts │ │ ├── number.ts │ │ ├── os.ts │ │ ├── route.ts │ │ └── transitions.ts │ ├── vite-env.d.ts │ ├── web-component.tsx │ └── widget │ │ ├── Router.tsx │ │ ├── ShadowDomAndProviders.tsx │ │ ├── Widget.tsx │ │ ├── initAmplitude.ts │ │ ├── initSentry.ts │ │ ├── theme.ts │ │ ├── useInitDefaultRoute.ts │ │ └── useInitWidget.ts │ ├── tsconfig.json │ ├── tsconfig.webpack.json │ ├── vite.config.ts │ ├── web-component │ ├── README.md │ ├── index.d.ts │ ├── package.json │ └── syncVersion.cjs │ └── webpack.config.js ├── vendor └── index.js └── yarn.lock /.changeset/README.md: -------------------------------------------------------------------------------- 1 | # Changesets 2 | 3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works 4 | with multi-package repos, or single-package repos to help you version and publish your code. You can 5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets) 6 | 7 | We have a quick list of common questions to get you started engaging with this project in 8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) 9 | -------------------------------------------------------------------------------- /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@3.0.1/schema.json", 3 | "changelog": "@changesets/cli/changelog", 4 | "commit": false, 5 | "fixed": [], 6 | "linked": [], 7 | "access": "restricted", 8 | "baseBranch": "main", 9 | "updateInternalDependencies": "patch", 10 | "ignore": [] 11 | } 12 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @codingki @NotJeremyLiu @toddkao @ericHgorski @daps94 2 | -------------------------------------------------------------------------------- /.github/workflows/eslint.yml: -------------------------------------------------------------------------------- 1 | name: ESLint Widget 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - 'packages/widget/**' 7 | 8 | jobs: 9 | eslint: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | - uses: actions/setup-node@v4 14 | with: 15 | node-version: lts/* 16 | cache: 'yarn' 17 | cache-dependency-path: yarn.lock 18 | - name: Install dependencies 19 | run: YARN_ENABLE_SCRIPTS=false yarn install --immutable 20 | working-directory: packages/widget 21 | - name: Run ESLint 22 | run: yarn lint 23 | working-directory: packages/widget 24 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - main 8 | 9 | concurrency: ${{ github.workflow }}-${{ github.ref }} 10 | 11 | permissions: 12 | actions: write 13 | contents: write 14 | id-token: write 15 | pull-requests: write 16 | 17 | jobs: 18 | release: 19 | name: Release 20 | runs-on: ubuntu-latest 21 | steps: 22 | - name: Checkout Repo 23 | uses: actions/checkout@v3 24 | 25 | - uses: actions/setup-node@v4 26 | with: 27 | node-version: lts/* 28 | cache: 'yarn' 29 | cache-dependency-path: yarn.lock 30 | 31 | - name: Install dependencies 32 | run: YARN_ENABLE_SCRIPTS=false yarn install --immutable 33 | 34 | - name: Create Release Pull Request or Publish to npm 35 | id: changesets 36 | uses: changesets/action@v1 37 | with: 38 | publish: yarn release 39 | env: 40 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 41 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 42 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 43 | -------------------------------------------------------------------------------- /.github/workflows/release-web-component.yml: -------------------------------------------------------------------------------- 1 | 2 | name: Release Web Component 3 | 4 | on: 5 | push: 6 | branches: 7 | - main 8 | 9 | jobs: 10 | publish: 11 | # Optional: Run only if Changesets released (can check for specific commit message prefix) 12 | if: contains(github.event.head_commit.message, 'Version Packages') 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout repository 16 | uses: actions/checkout@v3 17 | 18 | - uses: actions/setup-node@v4 19 | with: 20 | node-version: lts/* 21 | cache: 'yarn' 22 | cache-dependency-path: yarn.lock 23 | 24 | - name: Install dependencies 25 | run: YARN_ENABLE_SCRIPTS=false yarn install --immutable 26 | - name: Build 27 | run: yarn build 28 | - name: Configure .npmrc for authentication 29 | run: echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc 30 | env: 31 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 32 | - name: Publish widget web component 33 | working-directory: packages/widget 34 | run: yarn publish:web-component 35 | env: 36 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 37 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 38 | -------------------------------------------------------------------------------- /.github/workflows/sync-staging.yml: -------------------------------------------------------------------------------- 1 | name: Sync (Staging) 2 | on: 3 | push: 4 | branches: 5 | - main 6 | 7 | jobs: 8 | sync-branches: 9 | runs-on: ubuntu-latest 10 | name: Syncing branches 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v2 14 | - name: Set up Node 15 | uses: actions/setup-node@v1 16 | with: 17 | node-version: 12 18 | - name: Opening pull request 19 | id: pull 20 | uses: tretuna/sync-branches@1.4.0 21 | with: 22 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 23 | FROM_BRANCH: 'main' 24 | TO_BRANCH: 'staging' 25 | CONTENT_COMPARISON: true 26 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | on: 3 | pull_request: 4 | push: 5 | branches: 6 | - main 7 | - staging 8 | 9 | jobs: 10 | test: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v3 15 | 16 | - uses: actions/setup-node@v4 17 | with: 18 | node-version: lts/* 19 | cache: 'yarn' 20 | cache-dependency-path: yarn.lock 21 | 22 | - name: Fetch submodule 23 | run: git submodule update --init --recursive 24 | 25 | - name: Install dependencies 26 | run: YARN_ENABLE_SCRIPTS=false yarn install --immutable 27 | 28 | - name: Generate cosmjs types 29 | run: yarn run codegen 30 | working-directory: ./packages/client 31 | 32 | - name: Test 33 | run: yarn run test 34 | working-directory: ./packages/client 35 | -------------------------------------------------------------------------------- /.github/workflows/widget-tests.yml: -------------------------------------------------------------------------------- 1 | name: Widget Tests 2 | on: 3 | pull_request: 4 | push: 5 | branches: 6 | - main 7 | - staging 8 | 9 | jobs: 10 | e2e-test: 11 | timeout-minutes: 20 12 | runs-on: ubuntu-latest 13 | container: 14 | image: mcr.microsoft.com/playwright:v1.52.0-noble 15 | options: --user 1001 16 | ports: 17 | - 5173:5173 18 | env: 19 | PLAYWRIGHT_BROWSERS_PATH: ~/.cache/ms-playwright 20 | steps: 21 | - uses: actions/checkout@v4 22 | - uses: actions/setup-node@v4 23 | with: 24 | node-version: lts/* 25 | cache: 'yarn' 26 | cache-dependency-path: yarn.lock 27 | - name: Cache Playwright browsers 28 | id: playwright-cache 29 | uses: actions/cache@v3 30 | with: 31 | path: ~/.cache/ms-playwright 32 | key: ${{ runner.os }}-playwright-${{ hashFiles('yarn.lock') }} 33 | restore-keys: | 34 | ${{ runner.os }}-playwright- 35 | - name: Install dependencies 36 | run: YARN_ENABLE_SCRIPTS=false yarn install --immutable 37 | - name: Playwright install 38 | working-directory: ./packages/widget 39 | if: steps.playwright-cache.outputs.cache-hit != 'true' 40 | run: yarn playwright install 41 | - name: build client lib 42 | run: yarn run build:client 43 | - name: Run e2e tests 44 | env: 45 | WORD_PHRASE_KEY: ${{ secrets.WORD_PHRASE_KEY }} 46 | run: yarn test-widget 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | node_modules 3 | /.pnp 4 | .pnp.js 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | dist 11 | lib 12 | main 13 | mjs 14 | module 15 | build 16 | tsconfig.tsbuildinfo 17 | 18 | # misc 19 | .DS_Store 20 | .env.local 21 | .env.development.local 22 | .env.test.local 23 | .env.production.local 24 | 25 | npm-debug.log* 26 | yarn-debug.log* 27 | yarn-error.log* 28 | .yarn/cache 29 | .yarn/install-state.gz 30 | 31 | yalc.lock 32 | .yalc 33 | 34 | .idea 35 | .node_repl_history 36 | .npm 37 | .turbo 38 | 39 | .env 40 | 41 | packages/widget/__tests__/downloads* 42 | packages/widget/test-results* -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "vendor/cosmos-proto"] 2 | path = vendor/cosmos-proto 3 | url = https://github.com/cosmos/cosmos-proto.git 4 | [submodule "vendor/cosmos-sdk"] 5 | path = vendor/cosmos-sdk 6 | url = https://github.com/cosmos/cosmos-sdk.git 7 | [submodule "vendor/evmos"] 8 | path = vendor/evmos 9 | url = https://github.com/evmos/evmos.git 10 | [submodule "vendor/noble-cctp"] 11 | path = vendor/noble-cctp 12 | url = https://github.com/circlefin/noble-cctp.git 13 | [submodule "vendor/wasmd"] 14 | path = vendor/wasmd 15 | url = https://github.com/CosmWasm/wasmd.git 16 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true, 3 | "editor.defaultFormatter": "dbaeumer.vscode-eslint", 4 | "editor.tabSize": 2, 5 | "eslint.workingDirectories": [{ "mode": "auto" }], 6 | "editor.codeActionsOnSave": { 7 | "source.fixAll.eslint": "explicit" 8 | }, 9 | "eslint.format.enable": true, 10 | "[typescript]": { 11 | "editor.defaultFormatter": "dbaeumer.vscode-eslint" 12 | }, 13 | "[typescriptreact]": { 14 | "editor.defaultFormatter": "dbaeumer.vscode-eslint" 15 | }, 16 | "[javascript]": { 17 | "editor.defaultFormatter": "dbaeumer.vscode-eslint" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules 2 | 3 | plugins: 4 | - path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs 5 | spec: '@yarnpkg/plugin-workspace-tools' 6 | 7 | yarnPath: .yarn/releases/yarn-3.2.3.cjs 8 | -------------------------------------------------------------------------------- /AGENTS.md: -------------------------------------------------------------------------------- 1 | # Repository Instructions 2 | 3 | ## All Codex PRs must include a changeset entry. 4 | 5 | 1. From the repository root run `npx changeset`. 6 | 2. Commit the generated file inside `.changeset/` with the rest of your changes. 7 | 8 | ## Testing rules for `packages/widget` 9 | 10 | 1. Name each test file after the file being tested and append the `.test.ts` suffix. 11 | 2. Import testing utilities from `@playwright/test`. 12 | 3. Include multiple test cases in every file. 13 | 14 | ## Best Practices 15 | 16 | 1. Keep PRs focused on a single change. 17 | 2. Write descriptive commit messages in the imperative mood, e.g. "Add widget tests". 18 | 3. Favor small helper functions over repeated logic. 19 | 4. Document complex code with comments. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Skip Go 2 | 3 | ## Project Structure 4 | 5 | ```sh 6 | ├── examples 7 | │   └── nextjs 8 | ├── packages 9 | │   ├── client 10 | │   └── widget 11 | ``` 12 | 13 | - `client` (@skip-go/client): The core package of the project. Contains the core logic, api fetch and types, helper functions, signing, etc. 14 | - `widget` (@skip-go/widget): React library that contains the widget component. 15 | - `examples`: The example app of the project 16 | 17 | ### How the package consumed 18 | 19 | `@skip-go/client` -> `@skip-go/widget` -> `examples/nextjs` 20 | 21 | ## Dev Setup 22 | 23 | ### Install dependencies 24 | 25 | ```bash 26 | yarn 27 | ``` 28 | 29 | ### Start the development server 30 | 31 | It will run the example app on `http://localhost:3000` 32 | 33 | ```bash 34 | yarn dev 35 | ``` 36 | 37 | ### Build the packages 38 | 39 | ```bash 40 | yarn build 41 | ``` 42 | 43 | ### Updating changelog 44 | 45 | ``` 46 | npx changeset 47 | ``` 48 | 49 | ## Docs 50 | 51 | - [Client](./packages/client/README.md) 52 | - [Widget](./packages/widget/README.md) 53 | 54 | ## Examples 55 | 56 | - Widget: https://github.com/skip-mev/go-widget-example-next-js-app 57 | - Client: https://github.com/skip-mev/skip-next-simple-example 58 | -------------------------------------------------------------------------------- /docs/api-reference/prod/fungible/get-v2fungibleassets.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Get /v2/fungible/assets 3 | openapi: swagger get /v2/fungible/assets 4 | --- -------------------------------------------------------------------------------- /docs/api-reference/prod/fungible/get-v2fungiblevenues.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Get /v2/fungible/venues 3 | openapi: swagger get /v2/fungible/venues 4 | --- -------------------------------------------------------------------------------- /docs/api-reference/prod/fungible/post-v2fungibleassets_between_chains.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Post /v2/fungible/assets_between_chains 3 | openapi: swagger post /v2/fungible/assets_between_chains 4 | --- -------------------------------------------------------------------------------- /docs/api-reference/prod/fungible/post-v2fungibleassets_from_source.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Post /v2/fungible/assets_from_source 3 | openapi: swagger post /v2/fungible/assets_from_source 4 | --- -------------------------------------------------------------------------------- /docs/api-reference/prod/fungible/post-v2fungibleibc_origin_assets.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Post /v2/fungible/ibc_origin_assets 3 | openapi: swagger post /v2/fungible/ibc_origin_assets 4 | --- -------------------------------------------------------------------------------- /docs/api-reference/prod/fungible/post-v2fungiblemsgs.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Post /v2/fungible/msgs 3 | openapi: swagger post /v2/fungible/msgs 4 | --- -------------------------------------------------------------------------------- /docs/api-reference/prod/fungible/post-v2fungiblemsgs_direct.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Post /v2/fungible/msgs_direct 3 | openapi: swagger post /v2/fungible/msgs_direct 4 | --- -------------------------------------------------------------------------------- /docs/api-reference/prod/fungible/post-v2fungibleroute.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Post /v2/fungible/route 3 | openapi: swagger post /v2/fungible/route 4 | --- -------------------------------------------------------------------------------- /docs/api-reference/prod/info/get-v2infobridges.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Get /v2/info/bridges 3 | openapi: swagger get /v2/info/bridges 4 | --- -------------------------------------------------------------------------------- /docs/api-reference/prod/info/get-v2infochains.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Get /v2/info/chains 3 | openapi: swagger get /v2/info/chains 4 | --- -------------------------------------------------------------------------------- /docs/api-reference/prod/info/post-v2infobalances.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Post /v2/info/balances 3 | openapi: swagger post /v2/info/balances 4 | --- -------------------------------------------------------------------------------- /docs/api-reference/prod/transaction/get-v2txstatus.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Get /v2/tx/status 3 | openapi: swagger get /v2/tx/status 4 | --- -------------------------------------------------------------------------------- /docs/api-reference/prod/transaction/post-v2txsubmit.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Post /v2/tx/submit 3 | openapi: swagger post /v2/tx/submit 4 | --- -------------------------------------------------------------------------------- /docs/api-reference/prod/transaction/post-v2txtrack.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Post /v2/tx/track 3 | openapi: swagger post /v2/tx/track 4 | --- -------------------------------------------------------------------------------- /docs/client/advanced-features.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Advanced Features' 3 | description: 'This page details advanced features and utilities in the Skip Go client library.' 4 | --- 5 | 6 | ## Adding custom messages before or after route execution 7 | 8 | The `executeRoute` method now accepts `beforeMsg` and `afterMsg` parameter to allow for the execution of custom Cosmos messages before and/or after the route is executed. This is useful for executing custom messages that are not part of the route definition. 9 | 10 | 11 | ```typescript 12 | const msg = JSON.stringify({ 13 | fromAddress: 'cosmos1...', // Replace with sender address 14 | toAddress: 'cosmos1...', // Replace with recipient address 15 | amount: [{ 16 | denom: 'uatom', // Replace with the actual denom, e.g., 'uatom' for ATOM 17 | amount: '1000000' // Replace with the actual amount (in smallest unit, e.g., micro-ATOM) 18 | }] 19 | }); 20 | 21 | await executeRoute({ 22 | route, 23 | userAddresses, 24 | beforeMsg: { msg, msgTypeUrl: '/cosmos.bank.v1beta1.MsgSend' } 25 | }); 26 | ``` 27 | ## Use the Go Fast Transfer system 28 | 29 | The `route` function accepts a `goFast` parameter to enable the Go Fast Transfers. Then pass this route to the `executeRoute` method to execute. 30 | ```typescript 31 | import { executeRoute, route } from '@skip-go/client'; 32 | 33 | const route = await route({ 34 | goFast: true 35 | ...otherParams, 36 | }); 37 | 38 | await executeRoute({ 39 | route, 40 | ...otherParams, 41 | }); 42 | ``` -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skip-mev/skip-go/72247b2ef65a8c6ba340bf069ad06a0bb36dd43c/docs/favicon.ico -------------------------------------------------------------------------------- /docs/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skip-mev/skip-go/72247b2ef65a8c6ba340bf069ad06a0bb36dd43c/docs/favicon.png -------------------------------------------------------------------------------- /docs/general/upcoming-features.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Upcoming Features" 3 | description: "This page highlights a few of the features we're cooking up 🧑‍🍳. Please get in touch with us if you'd like us to prioritize any of these or add something else to our roadmap" 4 | --- 5 | 6 | #### New bridges 7 | 8 | * Wormhole 9 | * Union 10 | * Bifrost 11 | * Synapse 12 | * Initia's native bridge 13 | 14 | #### More DEXes 15 | 16 | We're currently adding support for: 17 | 18 | * Kujira's native DEX 19 | * Stride (conversions between TOKEN + stTOKEN) 20 | 21 | 22 | 23 | **Have questions or feedback? Help us get better!** 24 | 25 | Join [our Discord](https://skip.build/discord) and select the "Skip Go Developer" role to share your questions and feedback. 26 | -------------------------------------------------------------------------------- /docs/images/12bf3f9-Screen_Shot_2023-07-25_at_3.26.30_PM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skip-mev/skip-go/72247b2ef65a8c6ba340bf069ad06a0bb36dd43c/docs/images/12bf3f9-Screen_Shot_2023-07-25_at_3.26.30_PM.png -------------------------------------------------------------------------------- /docs/images/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skip-mev/skip-go/72247b2ef65a8c6ba340bf069ad06a0bb36dd43c/docs/images/2.png -------------------------------------------------------------------------------- /docs/images/2477aba-Screen_Shot_2023-07-25_at_3.48.15_PM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skip-mev/skip-go/72247b2ef65a8c6ba340bf069ad06a0bb36dd43c/docs/images/2477aba-Screen_Shot_2023-07-25_at_3.48.15_PM.png -------------------------------------------------------------------------------- /docs/images/3dde446-case3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skip-mev/skip-go/72247b2ef65a8c6ba340bf069ad06a0bb36dd43c/docs/images/3dde446-case3.jpg -------------------------------------------------------------------------------- /docs/images/3ff1176-Screen_Shot_2023-07-25_at_11.46.38_AM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skip-mev/skip-go/72247b2ef65a8c6ba340bf069ad06a0bb36dd43c/docs/images/3ff1176-Screen_Shot_2023-07-25_at_11.46.38_AM.png -------------------------------------------------------------------------------- /docs/images/443a393-Screen_Shot_2023-07-25_at_11.46.47_AM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skip-mev/skip-go/72247b2ef65a8c6ba340bf069ad06a0bb36dd43c/docs/images/443a393-Screen_Shot_2023-07-25_at_11.46.47_AM.png -------------------------------------------------------------------------------- /docs/images/51dc78a-Screen_Shot_2023-07-25_at_3.04.14_PM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skip-mev/skip-go/72247b2ef65a8c6ba340bf069ad06a0bb36dd43c/docs/images/51dc78a-Screen_Shot_2023-07-25_at_3.04.14_PM.png -------------------------------------------------------------------------------- /docs/images/568550d-case2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skip-mev/skip-go/72247b2ef65a8c6ba340bf069ad06a0bb36dd43c/docs/images/568550d-case2.jpg -------------------------------------------------------------------------------- /docs/images/5b993ce-swap-warning-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skip-mev/skip-go/72247b2ef65a8c6ba340bf069ad06a0bb36dd43c/docs/images/5b993ce-swap-warning-image.png -------------------------------------------------------------------------------- /docs/images/5c93590-error-type-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skip-mev/skip-go/72247b2ef65a8c6ba340bf069ad06a0bb36dd43c/docs/images/5c93590-error-type-2.png -------------------------------------------------------------------------------- /docs/images/66a9d2b-Screen_Shot_2023-07-25_at_9.50.00_AM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skip-mev/skip-go/72247b2ef65a8c6ba340bf069ad06a0bb36dd43c/docs/images/66a9d2b-Screen_Shot_2023-07-25_at_9.50.00_AM.png -------------------------------------------------------------------------------- /docs/images/6744b96-error-1-diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skip-mev/skip-go/72247b2ef65a8c6ba340bf069ad06a0bb36dd43c/docs/images/6744b96-error-1-diagram.png -------------------------------------------------------------------------------- /docs/images/78d8d3f-CleanShot_2024-05-23_at_15.33.182x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skip-mev/skip-go/72247b2ef65a8c6ba340bf069ad06a0bb36dd43c/docs/images/78d8d3f-CleanShot_2024-05-23_at_15.33.182x.png -------------------------------------------------------------------------------- /docs/images/9afddc0-case1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skip-mev/skip-go/72247b2ef65a8c6ba340bf069ad06a0bb36dd43c/docs/images/9afddc0-case1.jpg -------------------------------------------------------------------------------- /docs/images/a61a075-swap-warning-page-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skip-mev/skip-go/72247b2ef65a8c6ba340bf069ad06a0bb36dd43c/docs/images/a61a075-swap-warning-page-image.png -------------------------------------------------------------------------------- /docs/images/a83beaf-CleanShot_2024-05-23_at_15.59.452x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skip-mev/skip-go/72247b2ef65a8c6ba340bf069ad06a0bb36dd43c/docs/images/a83beaf-CleanShot_2024-05-23_at_15.59.452x.png -------------------------------------------------------------------------------- /docs/images/b42b522-CleanShot_2024-05-23_at_15.38.522x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skip-mev/skip-go/72247b2ef65a8c6ba340bf069ad06a0bb36dd43c/docs/images/b42b522-CleanShot_2024-05-23_at_15.38.522x.png -------------------------------------------------------------------------------- /docs/images/ca61685-CleanShot_2024-05-23_at_15.28.242x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skip-mev/skip-go/72247b2ef65a8c6ba340bf069ad06a0bb36dd43c/docs/images/ca61685-CleanShot_2024-05-23_at_15.28.242x.png -------------------------------------------------------------------------------- /docs/images/cc4fe76-Screenshot_2024-03-07_at_9.12.25_AM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skip-mev/skip-go/72247b2ef65a8c6ba340bf069ad06a0bb36dd43c/docs/images/cc4fe76-Screenshot_2024-03-07_at_9.12.25_AM.png -------------------------------------------------------------------------------- /docs/images/eb72804-Screen_Shot_2023-07-25_at_3.59.53_PM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skip-mev/skip-go/72247b2ef65a8c6ba340bf069ad06a0bb36dd43c/docs/images/eb72804-Screen_Shot_2023-07-25_at_3.59.53_PM.png -------------------------------------------------------------------------------- /docs/images/shareable-links.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skip-mev/skip-go/72247b2ef65a8c6ba340bf069ad06a0bb36dd43c/docs/images/shareable-links.png -------------------------------------------------------------------------------- /docs/logo/dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /docs/logo/light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /docs/release-notes/overview.mdx: -------------------------------------------------------------------------------- 1 | Stay up to date with Skip Go's latest features and improvements. 2 | 3 | 4 | 5 | 6 | A library that streamlines interaction with the Skip Go API, enabling cross-chain swaps and transfers across multiple ecosystems 7 | 8 | 9 | Seamless cross-chain bridging, swapping, and transferring functionality in an easy to integrate React or Web component 10 | 11 | 12 | REST endpoints for low-level control over your interactions. 13 | 14 | 15 | -------------------------------------------------------------------------------- /docs/style.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Inconsolata:wght@200..900&display=swap'); 2 | 3 | h1, h2, h3, #navbar { 4 | font-family: "Inconsolata", monospace; 5 | } 6 | 7 | .dark #navbar { 8 | background-color: #121213; 9 | } 10 | -------------------------------------------------------------------------------- /docs/widget/faq.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'FAQ' 3 | --- 4 | 5 | ## How do I fix the "Buffer is not defined" error? 6 | 7 | If you see a 'Buffer is not defined' error when using @skip-go/widget, it's likely because the widget depends on Node.js modules that aren't available in the browser. To fix this, you'll need to add polyfills for those modules. 8 | Here are some polyfill plugins for common environments: 9 | 10 | - [Webpack](https://www.npmjs.com/package/node-polyfill-webpack-plugin) 11 | - [Rollup](https://www.npmjs.com/package/rollup-plugin-polyfill-node) 12 | - [Vite](https://www.npmjs.com/package/vite-plugin-node-polyfills) 13 | - [ESBuild](https://www.npmjs.com/package/esbuild-plugins-node-modules-polyfill) 14 | 15 | ## Should I put the widget inside a container with a fixed size? 16 | 17 | It is recommended to wrap the widget with a container element that has a fixed size. This helps to prevent layout shifting as the widget uses the shadow-dom (which needs to be rendered client-side). We recommend a height of `640px` and a width of `500px`. -------------------------------------------------------------------------------- /examples/client/README.md: -------------------------------------------------------------------------------- 1 | # client 2 | -------------------------------------------------------------------------------- /examples/client/index.ts: -------------------------------------------------------------------------------- 1 | import { route, setApiOptions, getRecommendedGasPrice } from "@skip-go/client"; 2 | 3 | const getRoute = async () => { 4 | setApiOptions(); 5 | 6 | const response = await route({ 7 | sourceAssetDenom: "uatom", 8 | sourceAssetChainId: "cosmoshub-4", 9 | destAssetDenom: "uusdc", 10 | destAssetChainId: "noble-1", 11 | allowUnsafe: true, 12 | experimentalFeatures: ["stargate", "eureka"], 13 | allowMultiTx: true, 14 | smartRelay: true, 15 | smartSwapOptions: { splitRoutes: true, evmSwaps: true }, 16 | goFast: true, 17 | amountIn: "1000000", 18 | }); 19 | console.log(response); 20 | } 21 | 22 | const getRecGasPrice = async () => { 23 | setApiOptions(); 24 | const response = await getRecommendedGasPrice({ 25 | chainId: "osmosis-1" 26 | }); 27 | console.log(response); 28 | } 29 | 30 | // const getFeeInfo = async () => { 31 | // const response = await getFeeInfoForChain("dymension_1100-1"); 32 | // console.log(response); 33 | // } 34 | 35 | getRoute(); 36 | 37 | // getFeeInfo(); 38 | 39 | // getRecGasPrice(); -------------------------------------------------------------------------------- /examples/client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "client", 3 | "private": true, 4 | "packageManager": "yarn@3.2.3", 5 | "dependencies": { 6 | "@skip-go/client": "workspace:^", 7 | "typescript": "^5.8.3", 8 | "vite": "^6.3.2" 9 | }, 10 | "scripts": { 11 | "build": "vite build", 12 | "visualize": "VISUALIZE=true vite build" 13 | }, 14 | "devDependencies": { 15 | "rollup-plugin-visualizer": "^5.14.0", 16 | "vite-plugin-inspect": "^11.0.1", 17 | "vite-plugin-node-polyfills": "^0.23.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /examples/client/vite.config.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import inspect from 'vite-plugin-inspect'; 3 | import path from 'path'; 4 | import { visualizer } from 'rollup-plugin-visualizer'; 5 | import { nodePolyfills } from "vite-plugin-node-polyfills"; 6 | 7 | const enableVisualizer = process.env.VISUALIZE === 'true'; 8 | 9 | export default defineConfig({ 10 | plugins: [ 11 | inspect(), 12 | nodePolyfills(), 13 | ...(enableVisualizer ? [visualizer({ open: true })] : []), 14 | ], 15 | build: { 16 | lib: { 17 | entry: path.resolve(__dirname, 'index.ts'), 18 | name: 'ClientTest', 19 | fileName: 'client-test', 20 | formats: ['es'], 21 | }, 22 | rollupOptions: { 23 | output: { 24 | manualChunks: undefined, 25 | } 26 | } 27 | } 28 | }); -------------------------------------------------------------------------------- /examples/nextjs/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.* 7 | .yarn/* 8 | !.yarn/patches 9 | !.yarn/plugins 10 | !.yarn/releases 11 | !.yarn/versions 12 | 13 | # testing 14 | /coverage 15 | 16 | # next.js 17 | /.next/ 18 | /out/ 19 | 20 | # production 21 | /build 22 | 23 | # misc 24 | .DS_Store 25 | *.pem 26 | 27 | # debug 28 | npm-debug.log* 29 | yarn-debug.log* 30 | yarn-error.log* 31 | 32 | # env files (can opt-in for committing if needed) 33 | .env* 34 | 35 | # vercel 36 | .vercel 37 | 38 | # typescript 39 | *.tsbuildinfo 40 | next-env.d.ts 41 | -------------------------------------------------------------------------------- /examples/nextjs/README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | # or 12 | pnpm dev 13 | # or 14 | bun dev 15 | ``` 16 | 17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 18 | 19 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. 20 | 21 | This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. 22 | 23 | ## Learn More 24 | 25 | To learn more about Next.js, take a look at the following resources: 26 | 27 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 28 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 29 | 30 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! 31 | 32 | ## Deploy on Vercel 33 | 34 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 35 | 36 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. 37 | -------------------------------------------------------------------------------- /examples/nextjs/next.config.ts: -------------------------------------------------------------------------------- 1 | import type { NextConfig } from "next"; 2 | 3 | const nextConfig: NextConfig = { 4 | /* config options here */ 5 | reactStrictMode: true, 6 | }; 7 | 8 | export default nextConfig; 9 | -------------------------------------------------------------------------------- /examples/nextjs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nextjs", 3 | "version": "0.1.111", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@skip-go/widget": "workspace:^", 13 | "eruda": "^3.4.1", 14 | "next": "15.0.3", 15 | "react": "^18.2.0", 16 | "react-dom": "^18.2.0" 17 | }, 18 | "devDependencies": { 19 | "@types/node": "^20", 20 | "@types/react": "^18", 21 | "@types/react-dom": "^18", 22 | "typescript": "^5" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /examples/nextjs/src/app/globals.css: -------------------------------------------------------------------------------- 1 | 2 | html, 3 | body { 4 | max-width: 100vw; 5 | overflow-x: hidden; 6 | } 7 | 8 | body { 9 | color: #ffffff; 10 | background: #171717; 11 | } 12 | 13 | * { 14 | box-sizing: border-box; 15 | padding: 0; 16 | margin: 0; 17 | } 18 | -------------------------------------------------------------------------------- /examples/nextjs/src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import "./globals.css"; 3 | 4 | export const metadata: Metadata = { 5 | title: "Skip Go Example", 6 | }; 7 | 8 | export default function RootLayout({ 9 | children, 10 | }: Readonly<{ 11 | children: React.ReactNode; 12 | }>) { 13 | return ( 14 | 15 | 16 | {children} 17 | 18 | 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /examples/nextjs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2017", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "noEmit": true, 9 | "esModuleInterop": true, 10 | "module": "esnext", 11 | "moduleResolution": "bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "jsx": "preserve", 15 | "incremental": true, 16 | "plugins": [ 17 | { 18 | "name": "next" 19 | } 20 | ], 21 | "paths": { 22 | "@/*": ["./src/*"] 23 | } 24 | }, 25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 26 | "exclude": ["node_modules"] 27 | } 28 | -------------------------------------------------------------------------------- /examples/nextjs/window.d.ts: -------------------------------------------------------------------------------- 1 | import { Window as KeplrWindow } from "@keplr-wallet/types"; 2 | 3 | declare global { 4 | interface Window extends KeplrWindow { 5 | ethereum: import("@metamask/providers").MetaMaskInpageProvider; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /examples/nuxtjs/.gitignore: -------------------------------------------------------------------------------- 1 | # Nuxt dev/build outputs 2 | .output 3 | .data 4 | .nuxt 5 | .nitro 6 | .cache 7 | dist 8 | 9 | # Node dependencies 10 | node_modules 11 | 12 | # Logs 13 | logs 14 | *.log 15 | 16 | # Misc 17 | .DS_Store 18 | .fleet 19 | .idea 20 | 21 | # Local env files 22 | .env 23 | .env.* 24 | !.env.example 25 | -------------------------------------------------------------------------------- /examples/nuxtjs/README.md: -------------------------------------------------------------------------------- 1 | # Nuxt 3 Minimal Starter 2 | 3 | Look at the [Nuxt 3 documentation](https://nuxt.com/docs/getting-started/introduction) to learn more. 4 | 5 | ## Setup 6 | 7 | Make sure to install the dependencies: 8 | 9 | ```bash 10 | # npm 11 | npm install 12 | 13 | # pnpm 14 | pnpm install 15 | 16 | # yarn 17 | yarn install 18 | 19 | # bun 20 | bun install 21 | ``` 22 | 23 | ## Development Server 24 | 25 | Start the development server on `http://localhost:3000`: 26 | 27 | ```bash 28 | # npm 29 | npm run dev 30 | 31 | # pnpm 32 | pnpm run dev 33 | 34 | # yarn 35 | yarn dev 36 | 37 | # bun 38 | bun run dev 39 | ``` 40 | 41 | ## Production 42 | 43 | Build the application for production: 44 | 45 | ```bash 46 | # npm 47 | npm run build 48 | 49 | # pnpm 50 | pnpm run build 51 | 52 | # yarn 53 | yarn build 54 | 55 | # bun 56 | bun run build 57 | ``` 58 | 59 | Locally preview production build: 60 | 61 | ```bash 62 | # npm 63 | npm run preview 64 | 65 | # pnpm 66 | pnpm run preview 67 | 68 | # yarn 69 | yarn preview 70 | 71 | # bun 72 | bun run preview 73 | ``` 74 | 75 | Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information. 76 | -------------------------------------------------------------------------------- /examples/nuxtjs/app.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | -------------------------------------------------------------------------------- /examples/nuxtjs/nuxt.config.ts: -------------------------------------------------------------------------------- 1 | // https://nuxt.com/docs/api/configuration/nuxt-config 2 | export default defineNuxtConfig({ 3 | compatibilityDate: '2024-04-03', 4 | devtools: { enabled: true }, 5 | plugins: [ 6 | { src: './plugins/skip-widget.client.ts', mode: 'client' }, 7 | ], 8 | vue: { 9 | compilerOptions: { 10 | isCustomElement: (tag) => ['skip-widget'].includes(tag), 11 | }, 12 | }, 13 | }) 14 | -------------------------------------------------------------------------------- /examples/nuxtjs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nuxt-app", 3 | "private": true, 4 | "type": "module", 5 | "scripts": { 6 | "build": "nuxt build", 7 | "dev": "nuxt dev", 8 | "generate": "nuxt generate", 9 | "preview": "nuxt preview", 10 | "postinstall": "nuxt prepare" 11 | }, 12 | "dependencies": { 13 | "nuxt": "^3.13.0", 14 | "vue": "latest", 15 | "vue-router": "latest" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /examples/nuxtjs/plugins/skip-widget.client.ts: -------------------------------------------------------------------------------- 1 | import { defineNuxtPlugin } from '#app'; 2 | 3 | export default defineNuxtPlugin(() => { 4 | import('https://unpkg.com/@skip-go/widget-web-component/build/index.js') as any 5 | }) -------------------------------------------------------------------------------- /examples/nuxtjs/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skip-mev/skip-go/72247b2ef65a8c6ba340bf069ad06a0bb36dd43c/examples/nuxtjs/public/favicon.ico -------------------------------------------------------------------------------- /examples/nuxtjs/public/robots.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /examples/nuxtjs/server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../.nuxt/tsconfig.server.json" 3 | } 4 | -------------------------------------------------------------------------------- /examples/nuxtjs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // https://nuxt.com/docs/guide/concepts/typescript 3 | "extends": "./.nuxt/tsconfig.json" 4 | } 5 | -------------------------------------------------------------------------------- /examples/raw-html.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 |
10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /packages/client/README.md: -------------------------------------------------------------------------------- 1 | [![npm/v](https://badgen.net/npm/v/@skip-go/client)](https://www.npmjs.com/package/@skip-go/client) 2 | [![npm/dt](https://badgen.net/npm/dt/@skip-go/client?)](https://www.npmjs.com/package/@skip-go/client) 3 | [![stars](https://badgen.net/github/stars/skip-mev/skip-go?)](https://github.com/skip-mev/skip-go) 4 | 5 | # `@skip-go/client` 6 | 7 | TypeScript library that streamlines interaction with the Skip Go API, enabling cross-chain swaps and transfers across multiple ecosystems. 8 | 9 | ## Install 10 | 11 | ```bash 12 | npm install @skip-go/client 13 | ``` 14 | 15 | ```bash 16 | yarn add @skip-go/client 17 | ``` 18 | 19 | ## Usage 20 | 21 | Follow the [Getting Started](https://docs.skip.build/go/client/getting-started) guide to begin your integration. 22 | 23 | Example: [Skip go client example](https://github.com/skip-mev/skip-go-example) 24 | -------------------------------------------------------------------------------- /packages/client/e2e/utils.ts: -------------------------------------------------------------------------------- 1 | export const COSMOSHUB_ENDPOINT = "http://localhost:26657"; 2 | export const COSMOSHUB_FAUCET = "http://localhost:8083"; 3 | 4 | export const OSMOSIS_ENDPOINT = "http://localhost:26653"; 5 | export const OSMOSIS_FAUCET = "http://localhost:8082"; 6 | 7 | export const EVMOS_RPC_ENDPOINT = "http://localhost:26658"; 8 | export const EVMOS_REST_ENDPOINT = "http://localhost:1318"; 9 | 10 | export const INJECTIVE_RPC_ENDPOINT = "http://localhost:26659"; 11 | export const INJECTIVE_REST_ENDPOINT = "http://localhost:1319"; 12 | -------------------------------------------------------------------------------- /packages/client/jest.config.cjs: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-var-requires */ 2 | const { pathsToModuleNameMapper } = require("ts-jest"); 3 | const { compilerOptions } = require("./tsconfig"); 4 | 5 | const esModules = [ 6 | "@evmos/provider", 7 | "@evmos/transactions", 8 | "@evmos/eip712", 9 | "@evmos/proto", 10 | ].join("|"); 11 | 12 | /** @type {import('ts-jest').JestConfigWithTsJest} */ 13 | module.exports = { 14 | preset: "ts-jest", 15 | testEnvironment: "node", 16 | roots: [""], 17 | modulePaths: [compilerOptions.baseUrl], // <-- This will be set to 'baseUrl' value 18 | moduleNameMapper: pathsToModuleNameMapper( 19 | compilerOptions.paths /*, { prefix: '/' } */, 20 | ), 21 | transformIgnorePatterns: [`/node_modules/(?!${esModules})`], 22 | transform: { 23 | "^.+\\.js$": "babel-jest", 24 | }, 25 | }; 26 | -------------------------------------------------------------------------------- /packages/client/scripts/prepublish.cjs: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-var-requires */ 2 | 3 | const fs = require("fs/promises"); 4 | const path = require("path"); 5 | const packageJson = require("../package.json"); 6 | 7 | async function prepublish() { 8 | delete packageJson.scripts; 9 | delete packageJson.devDependencies; 10 | const targetPath = path.resolve(process.cwd(), "package.json"); 11 | await fs.writeFile(targetPath, JSON.stringify(packageJson, null, 2), { 12 | encoding: "utf-8", 13 | }); 14 | } 15 | 16 | void prepublish(); 17 | -------------------------------------------------------------------------------- /packages/client/src/amino/README.md: -------------------------------------------------------------------------------- 1 | # amino 2 | 3 | https://github.com/archmage-live/archmage-x/tree/develop/lib/network/cosm 4 | -------------------------------------------------------------------------------- /packages/client/src/amino/addresses.ts: -------------------------------------------------------------------------------- 1 | // https://github.com/archmage-live/archmage-x/blob/develop/lib/network/cosm/amino/addresses.ts 2 | 3 | import { 4 | pubkeyToRawAddress as cosmPubkeyToRawAddress, 5 | } from "@cosmjs/amino"; 6 | import type { Pubkey } from "@cosmjs/amino"; 7 | import { keccak256 } from "@cosmjs/crypto"; 8 | import { fromBase64, toBech32 } from "@cosmjs/encoding"; 9 | 10 | import { isEthSecp256k1Pubkey } from "./pubkey"; 11 | 12 | export function rawEthSecp256k1PubkeyToRawAddress( 13 | pubkeyData: Uint8Array, 14 | ): Uint8Array { 15 | if (pubkeyData.length !== 65) { 16 | throw new Error( 17 | `Invalid ETHSecp256k1 pubkey length (compressed): ${pubkeyData.length}`, 18 | ); 19 | } 20 | return keccak256(pubkeyData.slice(1)).slice(-20); 21 | } 22 | 23 | export function pubkeyToRawAddress(pubkey: Pubkey): Uint8Array { 24 | if (isEthSecp256k1Pubkey(pubkey)) { 25 | const pubkeyData = fromBase64(pubkey.value); 26 | return rawEthSecp256k1PubkeyToRawAddress(pubkeyData); 27 | } else { 28 | return cosmPubkeyToRawAddress(pubkey); 29 | } 30 | } 31 | 32 | export function pubkeyToAddress(pubkey: Pubkey, prefix: string): string { 33 | return toBech32(prefix, pubkeyToRawAddress(pubkey)); 34 | } 35 | -------------------------------------------------------------------------------- /packages/client/src/amino/encoding.ts: -------------------------------------------------------------------------------- 1 | // https://github.com/archmage-live/archmage-x/blob/develop/lib/network/cosm/amino/encoding.ts 2 | 3 | import { toBase64 } from "@cosmjs/encoding"; 4 | 5 | import { pubkeyType } from "./pubkey"; 6 | import type { EthSecp256k1Pubkey } from "./pubkey"; 7 | 8 | export function encodeEthSecp256k1Pubkey( 9 | pubkey: Uint8Array, 10 | ): EthSecp256k1Pubkey { 11 | if (pubkey.length !== 33 || (pubkey[0] !== 0x02 && pubkey[0] !== 0x03)) { 12 | throw new Error( 13 | "Public key must be compressed secp256k1, i.e. 33 bytes starting with 0x02 or 0x03", 14 | ); 15 | } 16 | return { 17 | type: pubkeyType.ethsecp256k1, 18 | value: toBase64(pubkey), 19 | }; 20 | } 21 | -------------------------------------------------------------------------------- /packages/client/src/amino/pubkey.ts: -------------------------------------------------------------------------------- 1 | // https://github.com/archmage-live/archmage-x/blob/develop/lib/network/cosm/amino/pubkey.ts 2 | 3 | import { 4 | pubkeyType as cosmPubkeyType, 5 | } from "@cosmjs/amino"; 6 | import type { Pubkey, SinglePubkey } from "@cosmjs/amino"; 7 | 8 | export interface EthSecp256k1Pubkey extends SinglePubkey { 9 | readonly type: "ethermint/PubKeyEthSecp256k1"; 10 | readonly value: string; 11 | } 12 | 13 | export function isEthSecp256k1Pubkey( 14 | pubkey: Pubkey, 15 | ): pubkey is EthSecp256k1Pubkey { 16 | return (pubkey as EthSecp256k1Pubkey).type === "ethermint/PubKeyEthSecp256k1"; 17 | } 18 | 19 | export const pubkeyType = { 20 | ethsecp256k1: "ethermint/PubKeyEthSecp256k1" as const, 21 | ...cosmPubkeyType, 22 | }; 23 | -------------------------------------------------------------------------------- /packages/client/src/api/getAssets.ts: -------------------------------------------------------------------------------- 1 | import type { Asset } from "../types/swaggerTypes"; 2 | import { ClientState } from "../state/clientState"; 3 | import { api } from "../utils/generateApi"; 4 | 5 | export const assets = api({ 6 | methodName: "assets", 7 | path: "v2/fungible/assets", 8 | transformResponse: (response) => { 9 | return transformAssetsMap(response.chainToAssetsMap); 10 | }, 11 | onSuccess: (response, options) => { 12 | if (options?.includeEvmAssets && options?.includeSvmAssets && options?.includeCw20Assets) { 13 | ClientState.skipAssets = response as Record; 14 | } 15 | }, 16 | }); 17 | 18 | export const transformAssetsMap = ( 19 | input?: Record, 20 | ): Record => 21 | Object.entries(input ?? {}).reduce( 22 | (acc, [chainId, { assets }]) => { 23 | acc[chainId] = (assets ?? []).map((asset) => asset); 24 | return acc; 25 | }, 26 | {} as Record, 27 | ); 28 | -------------------------------------------------------------------------------- /packages/client/src/api/getAssetsBetweenChains.ts: -------------------------------------------------------------------------------- 1 | import { api } from "../utils/generateApi"; 2 | 3 | export const assetsBetweenChains = api({ 4 | methodName: "fungibleAssetsBetweenChainsCreate", 5 | path: "v2/fungible/assets_between_chains", 6 | }); 7 | -------------------------------------------------------------------------------- /packages/client/src/api/getBridges.ts: -------------------------------------------------------------------------------- 1 | import { api } from "../utils/generateApi"; 2 | 3 | export const bridges = api({ 4 | methodName: "bridges", 5 | path: "v2/info/bridges", 6 | transformResponse: (response) => response.bridges, 7 | }); 8 | -------------------------------------------------------------------------------- /packages/client/src/api/getChains.ts: -------------------------------------------------------------------------------- 1 | import { ClientState } from "../state/clientState"; 2 | import { api } from "../utils/generateApi"; 3 | 4 | export const chains = api({ 5 | methodName: "chains", 6 | path: "v2/info/chains", 7 | transformResponse: (response) => response.chains, 8 | onSuccess: (response, options) => { 9 | if (options?.includeEvm && options?.includeSvm) { 10 | ClientState.skipChains = response; 11 | } 12 | }, 13 | }); 14 | -------------------------------------------------------------------------------- /packages/client/src/api/getVenues.ts: -------------------------------------------------------------------------------- 1 | import { api } from "../utils/generateApi"; 2 | 3 | export const venues = api({ 4 | methodName: "venues", 5 | path: "v2/fungible/venues", 6 | transformResponse: (response) => response.venues, 7 | }); 8 | -------------------------------------------------------------------------------- /packages/client/src/api/postAssetsFromSource.ts: -------------------------------------------------------------------------------- 1 | import { api } from "../utils/generateApi"; 2 | import { transformAssetsMap } from "./getAssets"; 3 | 4 | export const assetsFromSource = api({ 5 | methodName: "assetsFromSource", 6 | method: "post", 7 | path: "v2/fungible/assets_from_source", 8 | transformResponse(response) { 9 | return transformAssetsMap(response.destAssets); 10 | }, 11 | }); 12 | -------------------------------------------------------------------------------- /packages/client/src/api/postBalances.ts: -------------------------------------------------------------------------------- 1 | import { ClientState } from "../state/clientState"; 2 | import { api } from "../utils/generateApi"; 3 | import type { ApiRequest, ApiResponse } from "../utils/generateApi"; 4 | 5 | export const balances = api({ 6 | methodName: "balances", 7 | method: "post", 8 | path: "v2/info/balances", 9 | onSuccess: (response, options) => { 10 | if (!options) { 11 | ClientState.skipBalances = response; 12 | } 13 | }, 14 | }); 15 | 16 | export type BalanceRequest = ApiRequest<"balances">; 17 | export type BalanceResponse = ApiResponse<"balances">; 18 | -------------------------------------------------------------------------------- /packages/client/src/api/postIbcOriginAssets.ts: -------------------------------------------------------------------------------- 1 | import { api } from "../utils/generateApi"; 2 | 3 | export const ibcOriginAssets = api({ 4 | methodName: "ibcOriginAssets", 5 | method: "post", 6 | path: "v2/fungible/ibc_origin_assets", 7 | transformResponse: (response) => response.originAssets, 8 | }); 9 | -------------------------------------------------------------------------------- /packages/client/src/api/postMessages.ts: -------------------------------------------------------------------------------- 1 | import { api } from "../utils/generateApi"; 2 | import type { ApiRequest, ApiResponse } from "../utils/generateApi"; 3 | import { filterMessagesRequest } from "../utils/filterMessagesRequest"; 4 | import type { SkipApiOptions } from "src/state/apiState"; 5 | 6 | export const messages = async (request: MessagesRequest) => { 7 | return api({ 8 | methodName: "msgs", 9 | method: "post", 10 | path: "v2/fungible/msgs", 11 | })(filterMessagesRequest(request)); 12 | }; 13 | 14 | export type MessagesRequest = ApiRequest<"msgs"> & 15 | SkipApiOptions & { 16 | abortDuplicateRequests?: boolean; 17 | }; 18 | 19 | export type MessagesResponse = ApiResponse<"msgs">; 20 | -------------------------------------------------------------------------------- /packages/client/src/api/postMessagesDirect.ts: -------------------------------------------------------------------------------- 1 | import { api } from "../utils/generateApi"; 2 | 3 | export const messagesDirect = api({ 4 | methodName: "msgsDirect", 5 | method: "post", 6 | path: "v2/fungible/msgs_direct", 7 | }); 8 | -------------------------------------------------------------------------------- /packages/client/src/api/postRecommendAssets.ts: -------------------------------------------------------------------------------- 1 | import { api } from "../utils/generateApi"; 2 | 3 | export const recommendAssets = api({ 4 | methodName: "assetRecommendations", 5 | method: "post", 6 | path: "v2/fungible/recommend_assets", 7 | transformResponse(response) { 8 | return response.recommendationEntries; 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /packages/client/src/api/postRoute.ts: -------------------------------------------------------------------------------- 1 | import { api } from "../utils/generateApi"; 2 | import type { ApiRequest } from "../utils/generateApi"; 3 | import { ApiState } from "src/state/apiState"; 4 | import type { SkipApiOptions } from "src/state/apiState"; 5 | 6 | export const route = async (request: RouteRequest) => { 7 | const requestWithAffiliateFeeBps = { 8 | ...request, 9 | cumulativeAffiliateFeeBps: ApiState.cumulativeAffiliateFeeBPS, 10 | }; 11 | 12 | return api({ 13 | methodName: "route", 14 | method: "post", 15 | path: "v2/fungible/route", 16 | })(requestWithAffiliateFeeBps); 17 | }; 18 | 19 | export type RouteRequest = ApiRequest<"route"> & 20 | SkipApiOptions & { 21 | abortDuplicateRequests?: boolean; 22 | }; 23 | -------------------------------------------------------------------------------- /packages/client/src/api/postSubmit.ts: -------------------------------------------------------------------------------- 1 | import { api } from "../utils/generateApi"; 2 | import type { ApiRequest, ApiResponse } from "../utils/generateApi"; 3 | 4 | export const submit = api({ 5 | methodName: "submit", 6 | method: "post", 7 | path: "v2/tx/submit", 8 | }); 9 | 10 | export type SubmitRequest = ApiRequest<"submit">; 11 | export type SubmitResponse = ApiResponse<"submit">; 12 | -------------------------------------------------------------------------------- /packages/client/src/api/postSubmitTransaction.ts: -------------------------------------------------------------------------------- 1 | import { api } from "../utils/generateApi"; 2 | 3 | export const submitTransaction = api({ 4 | methodName: "submit", 5 | method: "post", 6 | path: "v2/tx/submit", 7 | }); 8 | -------------------------------------------------------------------------------- /packages/client/src/api/postTrackTransaction.ts: -------------------------------------------------------------------------------- 1 | import { pollingApi } from "../utils/generateApi"; 2 | import type { 3 | ApiRequest, 4 | PollingProps, 5 | } from "../utils/generateApi"; 6 | 7 | export type TrackTxRequest = ApiRequest<"status"> & TrackTxPollingProps; 8 | export type TrackTxPollingProps = Omit, "isSuccess" | "onError" | "onSuccess">; 9 | 10 | export const trackTransaction = ({ 11 | maxRetries, 12 | retryInterval, 13 | backoffMultiplier = 2.5, 14 | ...trackTxRequest 15 | }: TrackTxRequest) => { 16 | return pollingApi({ 17 | methodName: "track", 18 | path: "v2/tx/track", 19 | method: "post", 20 | maxRetries, 21 | retryInterval, 22 | backoffMultiplier, 23 | })(trackTxRequest); 24 | }; 25 | -------------------------------------------------------------------------------- /packages/client/src/api/postTransactionStatus.ts: -------------------------------------------------------------------------------- 1 | import { pollingApi } from "../utils/generateApi"; 2 | import type { ApiResponse } from "../utils/generateApi"; 3 | 4 | export const transactionStatus = pollingApi({ 5 | methodName: "status", 6 | path: "v2/tx/status", 7 | method: "get", 8 | }); 9 | 10 | export type TxStatusResponse = ApiResponse<"status">; 11 | -------------------------------------------------------------------------------- /packages/client/src/codegen/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skip-mev/skip-go/72247b2ef65a8c6ba340bf069ad06a0bb36dd43c/packages/client/src/codegen/.gitkeep -------------------------------------------------------------------------------- /packages/client/src/codegen/amino/amino.ts: -------------------------------------------------------------------------------- 1 | export {} -------------------------------------------------------------------------------- /packages/client/src/codegen/cosmos/auth/v1beta1/tx.amino.ts: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | import { MsgUpdateParams } from "./tx"; 3 | export const AminoConverter = { 4 | "/cosmos.auth.v1beta1.MsgUpdateParams": { 5 | aminoType: "cosmos-sdk/x/auth/MsgUpdateParams", 6 | toAmino: MsgUpdateParams.toAmino, 7 | fromAmino: MsgUpdateParams.fromAmino 8 | } 9 | }; -------------------------------------------------------------------------------- /packages/client/src/codegen/cosmos/auth/v1beta1/tx.registry.ts: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | import { GeneratedType } from "@cosmjs/proto-signing"; 3 | import { MsgUpdateParams } from "./tx"; 4 | export const registry: ReadonlyArray<[string, GeneratedType]> = [["/cosmos.auth.v1beta1.MsgUpdateParams", MsgUpdateParams]]; 5 | export const MessageComposer = { 6 | encoded: { 7 | updateParams(value: MsgUpdateParams) { 8 | return { 9 | typeUrl: "/cosmos.auth.v1beta1.MsgUpdateParams", 10 | value: MsgUpdateParams.encode(value).finish() 11 | }; 12 | } 13 | }, 14 | withTypeUrl: { 15 | updateParams(value: MsgUpdateParams) { 16 | return { 17 | typeUrl: "/cosmos.auth.v1beta1.MsgUpdateParams", 18 | value 19 | }; 20 | } 21 | }, 22 | toJSON: { 23 | updateParams(value: MsgUpdateParams) { 24 | return { 25 | typeUrl: "/cosmos.auth.v1beta1.MsgUpdateParams", 26 | value: MsgUpdateParams.toJSON(value) 27 | }; 28 | } 29 | }, 30 | fromJSON: { 31 | updateParams(value: any) { 32 | return { 33 | typeUrl: "/cosmos.auth.v1beta1.MsgUpdateParams", 34 | value: MsgUpdateParams.fromJSON(value) 35 | }; 36 | } 37 | }, 38 | fromPartial: { 39 | updateParams(value: MsgUpdateParams) { 40 | return { 41 | typeUrl: "/cosmos.auth.v1beta1.MsgUpdateParams", 42 | value: MsgUpdateParams.fromPartial(value) 43 | }; 44 | } 45 | } 46 | }; -------------------------------------------------------------------------------- /packages/client/src/codegen/cosmos/authz/v1beta1/tx.amino.ts: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | import { MsgGrant, MsgExec, MsgRevoke } from "./tx"; 3 | export const AminoConverter = { 4 | "/cosmos.authz.v1beta1.MsgGrant": { 5 | aminoType: "cosmos-sdk/MsgGrant", 6 | toAmino: MsgGrant.toAmino, 7 | fromAmino: MsgGrant.fromAmino 8 | }, 9 | "/cosmos.authz.v1beta1.MsgExec": { 10 | aminoType: "cosmos-sdk/MsgExec", 11 | toAmino: MsgExec.toAmino, 12 | fromAmino: MsgExec.fromAmino 13 | }, 14 | "/cosmos.authz.v1beta1.MsgRevoke": { 15 | aminoType: "cosmos-sdk/MsgRevoke", 16 | toAmino: MsgRevoke.toAmino, 17 | fromAmino: MsgRevoke.fromAmino 18 | } 19 | }; -------------------------------------------------------------------------------- /packages/client/src/codegen/cosmos/bank/v1beta1/tx.amino.ts: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | import { MsgSend, MsgMultiSend, MsgUpdateParams, MsgSetSendEnabled } from "./tx"; 3 | export const AminoConverter = { 4 | "/cosmos.bank.v1beta1.MsgSend": { 5 | aminoType: "cosmos-sdk/MsgSend", 6 | toAmino: MsgSend.toAmino, 7 | fromAmino: MsgSend.fromAmino 8 | }, 9 | "/cosmos.bank.v1beta1.MsgMultiSend": { 10 | aminoType: "cosmos-sdk/MsgMultiSend", 11 | toAmino: MsgMultiSend.toAmino, 12 | fromAmino: MsgMultiSend.fromAmino 13 | }, 14 | "/cosmos.bank.v1beta1.MsgUpdateParams": { 15 | aminoType: "cosmos-sdk/x/bank/MsgUpdateParams", 16 | toAmino: MsgUpdateParams.toAmino, 17 | fromAmino: MsgUpdateParams.fromAmino 18 | }, 19 | "/cosmos.bank.v1beta1.MsgSetSendEnabled": { 20 | aminoType: "cosmos-sdk/MsgSetSendEnabled", 21 | toAmino: MsgSetSendEnabled.toAmino, 22 | fromAmino: MsgSetSendEnabled.fromAmino 23 | } 24 | }; -------------------------------------------------------------------------------- /packages/client/src/codegen/cosmos/consensus/v1/tx.amino.ts: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | import { MsgUpdateParams } from "./tx"; 3 | export const AminoConverter = { 4 | "/cosmos.consensus.v1.MsgUpdateParams": { 5 | aminoType: "cosmos-sdk/MsgUpdateParams", 6 | toAmino: MsgUpdateParams.toAmino, 7 | fromAmino: MsgUpdateParams.fromAmino 8 | } 9 | }; -------------------------------------------------------------------------------- /packages/client/src/codegen/cosmos/consensus/v1/tx.registry.ts: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | import { GeneratedType } from "@cosmjs/proto-signing"; 3 | import { MsgUpdateParams } from "./tx"; 4 | export const registry: ReadonlyArray<[string, GeneratedType]> = [["/cosmos.consensus.v1.MsgUpdateParams", MsgUpdateParams]]; 5 | export const MessageComposer = { 6 | encoded: { 7 | updateParams(value: MsgUpdateParams) { 8 | return { 9 | typeUrl: "/cosmos.consensus.v1.MsgUpdateParams", 10 | value: MsgUpdateParams.encode(value).finish() 11 | }; 12 | } 13 | }, 14 | withTypeUrl: { 15 | updateParams(value: MsgUpdateParams) { 16 | return { 17 | typeUrl: "/cosmos.consensus.v1.MsgUpdateParams", 18 | value 19 | }; 20 | } 21 | }, 22 | toJSON: { 23 | updateParams(value: MsgUpdateParams) { 24 | return { 25 | typeUrl: "/cosmos.consensus.v1.MsgUpdateParams", 26 | value: MsgUpdateParams.toJSON(value) 27 | }; 28 | } 29 | }, 30 | fromJSON: { 31 | updateParams(value: any) { 32 | return { 33 | typeUrl: "/cosmos.consensus.v1.MsgUpdateParams", 34 | value: MsgUpdateParams.fromJSON(value) 35 | }; 36 | } 37 | }, 38 | fromPartial: { 39 | updateParams(value: MsgUpdateParams) { 40 | return { 41 | typeUrl: "/cosmos.consensus.v1.MsgUpdateParams", 42 | value: MsgUpdateParams.fromPartial(value) 43 | }; 44 | } 45 | } 46 | }; -------------------------------------------------------------------------------- /packages/client/src/codegen/cosmos/crisis/v1beta1/tx.amino.ts: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | import { MsgVerifyInvariant, MsgUpdateParams } from "./tx"; 3 | export const AminoConverter = { 4 | "/cosmos.crisis.v1beta1.MsgVerifyInvariant": { 5 | aminoType: "cosmos-sdk/MsgVerifyInvariant", 6 | toAmino: MsgVerifyInvariant.toAmino, 7 | fromAmino: MsgVerifyInvariant.fromAmino 8 | }, 9 | "/cosmos.crisis.v1beta1.MsgUpdateParams": { 10 | aminoType: "cosmos-sdk/x/crisis/MsgUpdateParams", 11 | toAmino: MsgUpdateParams.toAmino, 12 | fromAmino: MsgUpdateParams.fromAmino 13 | } 14 | }; -------------------------------------------------------------------------------- /packages/client/src/codegen/cosmos/distribution/v1beta1/tx.amino.ts: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | import { MsgSetWithdrawAddress, MsgWithdrawDelegatorReward, MsgWithdrawValidatorCommission, MsgFundCommunityPool, MsgUpdateParams, MsgCommunityPoolSpend } from "./tx"; 3 | export const AminoConverter = { 4 | "/cosmos.distribution.v1beta1.MsgSetWithdrawAddress": { 5 | aminoType: "cosmos-sdk/MsgModifyWithdrawAddress", 6 | toAmino: MsgSetWithdrawAddress.toAmino, 7 | fromAmino: MsgSetWithdrawAddress.fromAmino 8 | }, 9 | "/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward": { 10 | aminoType: "cosmos-sdk/MsgWithdrawDelegationReward", 11 | toAmino: MsgWithdrawDelegatorReward.toAmino, 12 | fromAmino: MsgWithdrawDelegatorReward.fromAmino 13 | }, 14 | "/cosmos.distribution.v1beta1.MsgWithdrawValidatorCommission": { 15 | aminoType: "cosmos-sdk/MsgWithdrawValidatorCommission", 16 | toAmino: MsgWithdrawValidatorCommission.toAmino, 17 | fromAmino: MsgWithdrawValidatorCommission.fromAmino 18 | }, 19 | "/cosmos.distribution.v1beta1.MsgFundCommunityPool": { 20 | aminoType: "cosmos-sdk/MsgFundCommunityPool", 21 | toAmino: MsgFundCommunityPool.toAmino, 22 | fromAmino: MsgFundCommunityPool.fromAmino 23 | }, 24 | "/cosmos.distribution.v1beta1.MsgUpdateParams": { 25 | aminoType: "cosmos-sdk/distribution/MsgUpdateParams", 26 | toAmino: MsgUpdateParams.toAmino, 27 | fromAmino: MsgUpdateParams.fromAmino 28 | }, 29 | "/cosmos.distribution.v1beta1.MsgCommunityPoolSpend": { 30 | aminoType: "cosmos-sdk/distr/MsgCommunityPoolSpend", 31 | toAmino: MsgCommunityPoolSpend.toAmino, 32 | fromAmino: MsgCommunityPoolSpend.fromAmino 33 | } 34 | }; -------------------------------------------------------------------------------- /packages/client/src/codegen/cosmos/evidence/v1beta1/tx.amino.ts: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | import { MsgSubmitEvidence } from "./tx"; 3 | export const AminoConverter = { 4 | "/cosmos.evidence.v1beta1.MsgSubmitEvidence": { 5 | aminoType: "cosmos-sdk/MsgSubmitEvidence", 6 | toAmino: MsgSubmitEvidence.toAmino, 7 | fromAmino: MsgSubmitEvidence.fromAmino 8 | } 9 | }; -------------------------------------------------------------------------------- /packages/client/src/codegen/cosmos/evidence/v1beta1/tx.registry.ts: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | import { GeneratedType } from "@cosmjs/proto-signing"; 3 | import { MsgSubmitEvidence } from "./tx"; 4 | export const registry: ReadonlyArray<[string, GeneratedType]> = [["/cosmos.evidence.v1beta1.MsgSubmitEvidence", MsgSubmitEvidence]]; 5 | export const MessageComposer = { 6 | encoded: { 7 | submitEvidence(value: MsgSubmitEvidence) { 8 | return { 9 | typeUrl: "/cosmos.evidence.v1beta1.MsgSubmitEvidence", 10 | value: MsgSubmitEvidence.encode(value).finish() 11 | }; 12 | } 13 | }, 14 | withTypeUrl: { 15 | submitEvidence(value: MsgSubmitEvidence) { 16 | return { 17 | typeUrl: "/cosmos.evidence.v1beta1.MsgSubmitEvidence", 18 | value 19 | }; 20 | } 21 | }, 22 | toJSON: { 23 | submitEvidence(value: MsgSubmitEvidence) { 24 | return { 25 | typeUrl: "/cosmos.evidence.v1beta1.MsgSubmitEvidence", 26 | value: MsgSubmitEvidence.toJSON(value) 27 | }; 28 | } 29 | }, 30 | fromJSON: { 31 | submitEvidence(value: any) { 32 | return { 33 | typeUrl: "/cosmos.evidence.v1beta1.MsgSubmitEvidence", 34 | value: MsgSubmitEvidence.fromJSON(value) 35 | }; 36 | } 37 | }, 38 | fromPartial: { 39 | submitEvidence(value: MsgSubmitEvidence) { 40 | return { 41 | typeUrl: "/cosmos.evidence.v1beta1.MsgSubmitEvidence", 42 | value: MsgSubmitEvidence.fromPartial(value) 43 | }; 44 | } 45 | } 46 | }; -------------------------------------------------------------------------------- /packages/client/src/codegen/cosmos/feegrant/v1beta1/tx.amino.ts: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | import { MsgGrantAllowance, MsgRevokeAllowance } from "./tx"; 3 | export const AminoConverter = { 4 | "/cosmos.feegrant.v1beta1.MsgGrantAllowance": { 5 | aminoType: "cosmos-sdk/MsgGrantAllowance", 6 | toAmino: MsgGrantAllowance.toAmino, 7 | fromAmino: MsgGrantAllowance.fromAmino 8 | }, 9 | "/cosmos.feegrant.v1beta1.MsgRevokeAllowance": { 10 | aminoType: "cosmos-sdk/MsgRevokeAllowance", 11 | toAmino: MsgRevokeAllowance.toAmino, 12 | fromAmino: MsgRevokeAllowance.fromAmino 13 | } 14 | }; -------------------------------------------------------------------------------- /packages/client/src/codegen/cosmos/gov/v1/tx.amino.ts: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | import { MsgSubmitProposal, MsgExecLegacyContent, MsgVote, MsgVoteWeighted, MsgDeposit, MsgUpdateParams } from "./tx"; 3 | export const AminoConverter = { 4 | "/cosmos.gov.v1.MsgSubmitProposal": { 5 | aminoType: "cosmos-sdk/v1/MsgSubmitProposal", 6 | toAmino: MsgSubmitProposal.toAmino, 7 | fromAmino: MsgSubmitProposal.fromAmino 8 | }, 9 | "/cosmos.gov.v1.MsgExecLegacyContent": { 10 | aminoType: "cosmos-sdk/v1/MsgExecLegacyContent", 11 | toAmino: MsgExecLegacyContent.toAmino, 12 | fromAmino: MsgExecLegacyContent.fromAmino 13 | }, 14 | "/cosmos.gov.v1.MsgVote": { 15 | aminoType: "cosmos-sdk/v1/MsgVote", 16 | toAmino: MsgVote.toAmino, 17 | fromAmino: MsgVote.fromAmino 18 | }, 19 | "/cosmos.gov.v1.MsgVoteWeighted": { 20 | aminoType: "cosmos-sdk/v1/MsgVoteWeighted", 21 | toAmino: MsgVoteWeighted.toAmino, 22 | fromAmino: MsgVoteWeighted.fromAmino 23 | }, 24 | "/cosmos.gov.v1.MsgDeposit": { 25 | aminoType: "cosmos-sdk/v1/MsgDeposit", 26 | toAmino: MsgDeposit.toAmino, 27 | fromAmino: MsgDeposit.fromAmino 28 | }, 29 | "/cosmos.gov.v1.MsgUpdateParams": { 30 | aminoType: "cosmos-sdk/x/gov/v1/MsgUpdateParams", 31 | toAmino: MsgUpdateParams.toAmino, 32 | fromAmino: MsgUpdateParams.fromAmino 33 | } 34 | }; -------------------------------------------------------------------------------- /packages/client/src/codegen/cosmos/gov/v1beta1/tx.amino.ts: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | import { MsgSubmitProposal, MsgVote, MsgVoteWeighted, MsgDeposit } from "./tx"; 3 | export const AminoConverter = { 4 | "/cosmos.gov.v1beta1.MsgSubmitProposal": { 5 | aminoType: "cosmos-sdk/MsgSubmitProposal", 6 | toAmino: MsgSubmitProposal.toAmino, 7 | fromAmino: MsgSubmitProposal.fromAmino 8 | }, 9 | "/cosmos.gov.v1beta1.MsgVote": { 10 | aminoType: "cosmos-sdk/MsgVote", 11 | toAmino: MsgVote.toAmino, 12 | fromAmino: MsgVote.fromAmino 13 | }, 14 | "/cosmos.gov.v1beta1.MsgVoteWeighted": { 15 | aminoType: "cosmos-sdk/MsgVoteWeighted", 16 | toAmino: MsgVoteWeighted.toAmino, 17 | fromAmino: MsgVoteWeighted.fromAmino 18 | }, 19 | "/cosmos.gov.v1beta1.MsgDeposit": { 20 | aminoType: "cosmos-sdk/MsgDeposit", 21 | toAmino: MsgDeposit.toAmino, 22 | fromAmino: MsgDeposit.fromAmino 23 | } 24 | }; -------------------------------------------------------------------------------- /packages/client/src/codegen/cosmos/mint/v1beta1/tx.amino.ts: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | import { MsgUpdateParams } from "./tx"; 3 | export const AminoConverter = { 4 | "/cosmos.mint.v1beta1.MsgUpdateParams": { 5 | aminoType: "cosmos-sdk/x/mint/MsgUpdateParams", 6 | toAmino: MsgUpdateParams.toAmino, 7 | fromAmino: MsgUpdateParams.fromAmino 8 | } 9 | }; -------------------------------------------------------------------------------- /packages/client/src/codegen/cosmos/mint/v1beta1/tx.registry.ts: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | import { GeneratedType } from "@cosmjs/proto-signing"; 3 | import { MsgUpdateParams } from "./tx"; 4 | export const registry: ReadonlyArray<[string, GeneratedType]> = [["/cosmos.mint.v1beta1.MsgUpdateParams", MsgUpdateParams]]; 5 | export const MessageComposer = { 6 | encoded: { 7 | updateParams(value: MsgUpdateParams) { 8 | return { 9 | typeUrl: "/cosmos.mint.v1beta1.MsgUpdateParams", 10 | value: MsgUpdateParams.encode(value).finish() 11 | }; 12 | } 13 | }, 14 | withTypeUrl: { 15 | updateParams(value: MsgUpdateParams) { 16 | return { 17 | typeUrl: "/cosmos.mint.v1beta1.MsgUpdateParams", 18 | value 19 | }; 20 | } 21 | }, 22 | toJSON: { 23 | updateParams(value: MsgUpdateParams) { 24 | return { 25 | typeUrl: "/cosmos.mint.v1beta1.MsgUpdateParams", 26 | value: MsgUpdateParams.toJSON(value) 27 | }; 28 | } 29 | }, 30 | fromJSON: { 31 | updateParams(value: any) { 32 | return { 33 | typeUrl: "/cosmos.mint.v1beta1.MsgUpdateParams", 34 | value: MsgUpdateParams.fromJSON(value) 35 | }; 36 | } 37 | }, 38 | fromPartial: { 39 | updateParams(value: MsgUpdateParams) { 40 | return { 41 | typeUrl: "/cosmos.mint.v1beta1.MsgUpdateParams", 42 | value: MsgUpdateParams.fromPartial(value) 43 | }; 44 | } 45 | } 46 | }; -------------------------------------------------------------------------------- /packages/client/src/codegen/cosmos/msg/v1/msg.ts: -------------------------------------------------------------------------------- 1 | export {} -------------------------------------------------------------------------------- /packages/client/src/codegen/cosmos/nft/v1beta1/tx.amino.ts: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | import { MsgSend } from "./tx"; 3 | export const AminoConverter = { 4 | "/cosmos.nft.v1beta1.MsgSend": { 5 | aminoType: "cosmos-sdk/MsgNFTSend", 6 | toAmino: MsgSend.toAmino, 7 | fromAmino: MsgSend.fromAmino 8 | } 9 | }; -------------------------------------------------------------------------------- /packages/client/src/codegen/cosmos/nft/v1beta1/tx.registry.ts: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | import { GeneratedType } from "@cosmjs/proto-signing"; 3 | import { MsgSend } from "./tx"; 4 | export const registry: ReadonlyArray<[string, GeneratedType]> = [["/cosmos.nft.v1beta1.MsgSend", MsgSend]]; 5 | export const MessageComposer = { 6 | encoded: { 7 | send(value: MsgSend) { 8 | return { 9 | typeUrl: "/cosmos.nft.v1beta1.MsgSend", 10 | value: MsgSend.encode(value).finish() 11 | }; 12 | } 13 | }, 14 | withTypeUrl: { 15 | send(value: MsgSend) { 16 | return { 17 | typeUrl: "/cosmos.nft.v1beta1.MsgSend", 18 | value 19 | }; 20 | } 21 | }, 22 | toJSON: { 23 | send(value: MsgSend) { 24 | return { 25 | typeUrl: "/cosmos.nft.v1beta1.MsgSend", 26 | value: MsgSend.toJSON(value) 27 | }; 28 | } 29 | }, 30 | fromJSON: { 31 | send(value: any) { 32 | return { 33 | typeUrl: "/cosmos.nft.v1beta1.MsgSend", 34 | value: MsgSend.fromJSON(value) 35 | }; 36 | } 37 | }, 38 | fromPartial: { 39 | send(value: MsgSend) { 40 | return { 41 | typeUrl: "/cosmos.nft.v1beta1.MsgSend", 42 | value: MsgSend.fromPartial(value) 43 | }; 44 | } 45 | } 46 | }; -------------------------------------------------------------------------------- /packages/client/src/codegen/cosmos/query/v1/query.ts: -------------------------------------------------------------------------------- 1 | export {} -------------------------------------------------------------------------------- /packages/client/src/codegen/cosmos/slashing/v1beta1/tx.amino.ts: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | import { MsgUnjail, MsgUpdateParams } from "./tx"; 3 | export const AminoConverter = { 4 | "/cosmos.slashing.v1beta1.MsgUnjail": { 5 | aminoType: "cosmos-sdk/MsgUnjail", 6 | toAmino: MsgUnjail.toAmino, 7 | fromAmino: MsgUnjail.fromAmino 8 | }, 9 | "/cosmos.slashing.v1beta1.MsgUpdateParams": { 10 | aminoType: "cosmos-sdk/x/slashing/MsgUpdateParams", 11 | toAmino: MsgUpdateParams.toAmino, 12 | fromAmino: MsgUpdateParams.fromAmino 13 | } 14 | }; -------------------------------------------------------------------------------- /packages/client/src/codegen/cosmos/upgrade/v1beta1/tx.amino.ts: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | import { MsgSoftwareUpgrade, MsgCancelUpgrade } from "./tx"; 3 | export const AminoConverter = { 4 | "/cosmos.upgrade.v1beta1.MsgSoftwareUpgrade": { 5 | aminoType: "cosmos-sdk/MsgSoftwareUpgrade", 6 | toAmino: MsgSoftwareUpgrade.toAmino, 7 | fromAmino: MsgSoftwareUpgrade.fromAmino 8 | }, 9 | "/cosmos.upgrade.v1beta1.MsgCancelUpgrade": { 10 | aminoType: "cosmos-sdk/MsgCancelUpgrade", 11 | toAmino: MsgCancelUpgrade.toAmino, 12 | fromAmino: MsgCancelUpgrade.fromAmino 13 | } 14 | }; -------------------------------------------------------------------------------- /packages/client/src/codegen/cosmos/vesting/v1beta1/tx.amino.ts: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | import { MsgCreateVestingAccount, MsgCreatePermanentLockedAccount, MsgCreatePeriodicVestingAccount } from "./tx"; 3 | export const AminoConverter = { 4 | "/cosmos.vesting.v1beta1.MsgCreateVestingAccount": { 5 | aminoType: "cosmos-sdk/MsgCreateVestingAccount", 6 | toAmino: MsgCreateVestingAccount.toAmino, 7 | fromAmino: MsgCreateVestingAccount.fromAmino 8 | }, 9 | "/cosmos.vesting.v1beta1.MsgCreatePermanentLockedAccount": { 10 | aminoType: "cosmos-sdk/MsgCreatePermLockedAccount", 11 | toAmino: MsgCreatePermanentLockedAccount.toAmino, 12 | fromAmino: MsgCreatePermanentLockedAccount.fromAmino 13 | }, 14 | "/cosmos.vesting.v1beta1.MsgCreatePeriodicVestingAccount": { 15 | aminoType: "cosmos-sdk/MsgCreatePeriodicVestingAccount", 16 | toAmino: MsgCreatePeriodicVestingAccount.toAmino, 17 | fromAmino: MsgCreatePeriodicVestingAccount.fromAmino 18 | } 19 | }; -------------------------------------------------------------------------------- /packages/client/src/codegen/ethermint/evm/v1/tx.amino.ts: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | import { MsgEthereumTx, MsgUpdateParams } from "./tx"; 3 | export const AminoConverter = { 4 | "/ethermint.evm.v1.MsgEthereumTx": { 5 | aminoType: "ethermint/MsgEthereumTx", 6 | toAmino: MsgEthereumTx.toAmino, 7 | fromAmino: MsgEthereumTx.fromAmino 8 | }, 9 | "/ethermint.evm.v1.MsgUpdateParams": { 10 | aminoType: "ethermint/MsgUpdateParams", 11 | toAmino: MsgUpdateParams.toAmino, 12 | fromAmino: MsgUpdateParams.fromAmino 13 | } 14 | }; -------------------------------------------------------------------------------- /packages/client/src/codegen/ethermint/feemarket/v1/tx.amino.ts: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | import { MsgUpdateParams } from "./tx"; 3 | export const AminoConverter = { 4 | "/ethermint.feemarket.v1.MsgUpdateParams": { 5 | aminoType: "ethermint/feemarket/MsgUpdateParams", 6 | toAmino: MsgUpdateParams.toAmino, 7 | fromAmino: MsgUpdateParams.fromAmino 8 | } 9 | }; -------------------------------------------------------------------------------- /packages/client/src/codegen/ethermint/feemarket/v1/tx.registry.ts: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | import { GeneratedType } from "@cosmjs/proto-signing"; 3 | import { MsgUpdateParams } from "./tx"; 4 | export const registry: ReadonlyArray<[string, GeneratedType]> = [["/ethermint.feemarket.v1.MsgUpdateParams", MsgUpdateParams]]; 5 | export const MessageComposer = { 6 | encoded: { 7 | updateParams(value: MsgUpdateParams) { 8 | return { 9 | typeUrl: "/ethermint.feemarket.v1.MsgUpdateParams", 10 | value: MsgUpdateParams.encode(value).finish() 11 | }; 12 | } 13 | }, 14 | withTypeUrl: { 15 | updateParams(value: MsgUpdateParams) { 16 | return { 17 | typeUrl: "/ethermint.feemarket.v1.MsgUpdateParams", 18 | value 19 | }; 20 | } 21 | }, 22 | toJSON: { 23 | updateParams(value: MsgUpdateParams) { 24 | return { 25 | typeUrl: "/ethermint.feemarket.v1.MsgUpdateParams", 26 | value: MsgUpdateParams.toJSON(value) 27 | }; 28 | } 29 | }, 30 | fromJSON: { 31 | updateParams(value: any) { 32 | return { 33 | typeUrl: "/ethermint.feemarket.v1.MsgUpdateParams", 34 | value: MsgUpdateParams.fromJSON(value) 35 | }; 36 | } 37 | }, 38 | fromPartial: { 39 | updateParams(value: MsgUpdateParams) { 40 | return { 41 | typeUrl: "/ethermint.feemarket.v1.MsgUpdateParams", 42 | value: MsgUpdateParams.fromPartial(value) 43 | }; 44 | } 45 | } 46 | }; -------------------------------------------------------------------------------- /packages/client/src/codegen/evmos/erc20/v1/tx.amino.ts: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | import { MsgConvertCoin, MsgConvertERC20, MsgUpdateParams } from "./tx"; 3 | export const AminoConverter = { 4 | "/evmos.erc20.v1.MsgConvertCoin": { 5 | aminoType: "evmos/MsgConvertCoin", 6 | toAmino: MsgConvertCoin.toAmino, 7 | fromAmino: MsgConvertCoin.fromAmino 8 | }, 9 | "/evmos.erc20.v1.MsgConvertERC20": { 10 | aminoType: "evmos/MsgConvertERC20", 11 | toAmino: MsgConvertERC20.toAmino, 12 | fromAmino: MsgConvertERC20.fromAmino 13 | }, 14 | "/evmos.erc20.v1.MsgUpdateParams": { 15 | aminoType: "evmos/erc20/MsgUpdateParams", 16 | toAmino: MsgUpdateParams.toAmino, 17 | fromAmino: MsgUpdateParams.fromAmino 18 | } 19 | }; -------------------------------------------------------------------------------- /packages/client/src/codegen/evmos/inflation/v1/tx.amino.ts: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | import { MsgUpdateParams } from "./tx"; 3 | export const AminoConverter = { 4 | "/evmos.inflation.v1.MsgUpdateParams": { 5 | aminoType: "evmos/inflation/MsgUpdateParams", 6 | toAmino: MsgUpdateParams.toAmino, 7 | fromAmino: MsgUpdateParams.fromAmino 8 | } 9 | }; -------------------------------------------------------------------------------- /packages/client/src/codegen/evmos/inflation/v1/tx.registry.ts: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | import { GeneratedType } from "@cosmjs/proto-signing"; 3 | import { MsgUpdateParams } from "./tx"; 4 | export const registry: ReadonlyArray<[string, GeneratedType]> = [["/evmos.inflation.v1.MsgUpdateParams", MsgUpdateParams]]; 5 | export const MessageComposer = { 6 | encoded: { 7 | updateParams(value: MsgUpdateParams) { 8 | return { 9 | typeUrl: "/evmos.inflation.v1.MsgUpdateParams", 10 | value: MsgUpdateParams.encode(value).finish() 11 | }; 12 | } 13 | }, 14 | withTypeUrl: { 15 | updateParams(value: MsgUpdateParams) { 16 | return { 17 | typeUrl: "/evmos.inflation.v1.MsgUpdateParams", 18 | value 19 | }; 20 | } 21 | }, 22 | toJSON: { 23 | updateParams(value: MsgUpdateParams) { 24 | return { 25 | typeUrl: "/evmos.inflation.v1.MsgUpdateParams", 26 | value: MsgUpdateParams.toJSON(value) 27 | }; 28 | } 29 | }, 30 | fromJSON: { 31 | updateParams(value: any) { 32 | return { 33 | typeUrl: "/evmos.inflation.v1.MsgUpdateParams", 34 | value: MsgUpdateParams.fromJSON(value) 35 | }; 36 | } 37 | }, 38 | fromPartial: { 39 | updateParams(value: MsgUpdateParams) { 40 | return { 41 | typeUrl: "/evmos.inflation.v1.MsgUpdateParams", 42 | value: MsgUpdateParams.fromPartial(value) 43 | }; 44 | } 45 | } 46 | }; -------------------------------------------------------------------------------- /packages/client/src/codegen/evmos/revenue/v1/tx.amino.ts: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | import { MsgRegisterRevenue, MsgUpdateRevenue, MsgCancelRevenue, MsgUpdateParams } from "./tx"; 3 | export const AminoConverter = { 4 | "/evmos.revenue.v1.MsgRegisterRevenue": { 5 | aminoType: "evmos/MsgRegisterRevenue", 6 | toAmino: MsgRegisterRevenue.toAmino, 7 | fromAmino: MsgRegisterRevenue.fromAmino 8 | }, 9 | "/evmos.revenue.v1.MsgUpdateRevenue": { 10 | aminoType: "evmos/MsgUpdateRevenue", 11 | toAmino: MsgUpdateRevenue.toAmino, 12 | fromAmino: MsgUpdateRevenue.fromAmino 13 | }, 14 | "/evmos.revenue.v1.MsgCancelRevenue": { 15 | aminoType: "evmos/MsgCancelRevenue", 16 | toAmino: MsgCancelRevenue.toAmino, 17 | fromAmino: MsgCancelRevenue.fromAmino 18 | }, 19 | "/evmos.revenue.v1.MsgUpdateParams": { 20 | aminoType: "evmos/MsgUpdateParams", 21 | toAmino: MsgUpdateParams.toAmino, 22 | fromAmino: MsgUpdateParams.fromAmino 23 | } 24 | }; -------------------------------------------------------------------------------- /packages/client/src/codegen/evmos/vesting/v2/tx.amino.ts: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | import { MsgCreateClawbackVestingAccount, MsgFundVestingAccount, MsgClawback, MsgUpdateVestingFunder, MsgConvertVestingAccount } from "./tx"; 3 | export const AminoConverter = { 4 | "/evmos.vesting.v2.MsgCreateClawbackVestingAccount": { 5 | aminoType: "evmos/MsgCreateClawbackVestingAccount", 6 | toAmino: MsgCreateClawbackVestingAccount.toAmino, 7 | fromAmino: MsgCreateClawbackVestingAccount.fromAmino 8 | }, 9 | "/evmos.vesting.v2.MsgFundVestingAccount": { 10 | aminoType: "evmos/MsgFundVestingAccount", 11 | toAmino: MsgFundVestingAccount.toAmino, 12 | fromAmino: MsgFundVestingAccount.fromAmino 13 | }, 14 | "/evmos.vesting.v2.MsgClawback": { 15 | aminoType: "evmos/MsgClawback", 16 | toAmino: MsgClawback.toAmino, 17 | fromAmino: MsgClawback.fromAmino 18 | }, 19 | "/evmos.vesting.v2.MsgUpdateVestingFunder": { 20 | aminoType: "evmos/MsgUpdateVestingFunder", 21 | toAmino: MsgUpdateVestingFunder.toAmino, 22 | fromAmino: MsgUpdateVestingFunder.fromAmino 23 | }, 24 | "/evmos.vesting.v2.MsgConvertVestingAccount": { 25 | aminoType: "evmos/MsgConvertVestingAccount", 26 | toAmino: MsgConvertVestingAccount.toAmino, 27 | fromAmino: MsgConvertVestingAccount.fromAmino 28 | } 29 | }; -------------------------------------------------------------------------------- /packages/client/src/codegen/gogoproto/gogo.ts: -------------------------------------------------------------------------------- 1 | export {} -------------------------------------------------------------------------------- /packages/client/src/codegen/google/api/annotations.ts: -------------------------------------------------------------------------------- 1 | export {} -------------------------------------------------------------------------------- /packages/client/src/codegen/initia/move/v1/tx.amino.ts: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | import { MsgExecute } from "./tx"; 3 | export const AminoConverter = { 4 | "/initia.move.v1.MsgExecute": { 5 | aminoType: "/initia.move.v1.MsgExecute", 6 | toAmino: MsgExecute.toAmino, 7 | fromAmino: MsgExecute.fromAmino 8 | } 9 | }; -------------------------------------------------------------------------------- /packages/client/src/codegen/initia/move/v1/tx.registry.ts: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | import { GeneratedType } from "@cosmjs/proto-signing"; 3 | import { MsgExecute } from "./tx"; 4 | export const registry: ReadonlyArray<[string, GeneratedType]> = [["/initia.move.v1.MsgExecute", MsgExecute]]; 5 | export const MessageComposer = { 6 | encoded: { 7 | execute(value: MsgExecute) { 8 | return { 9 | typeUrl: "/initia.move.v1.MsgExecute", 10 | value: MsgExecute.encode(value).finish() 11 | }; 12 | } 13 | }, 14 | withTypeUrl: { 15 | execute(value: MsgExecute) { 16 | return { 17 | typeUrl: "/initia.move.v1.MsgExecute", 18 | value 19 | }; 20 | } 21 | }, 22 | toJSON: { 23 | execute(value: MsgExecute) { 24 | return { 25 | typeUrl: "/initia.move.v1.MsgExecute", 26 | value: MsgExecute.toJSON(value) 27 | }; 28 | } 29 | }, 30 | fromJSON: { 31 | execute(value: any) { 32 | return { 33 | typeUrl: "/initia.move.v1.MsgExecute", 34 | value: MsgExecute.fromJSON(value) 35 | }; 36 | } 37 | }, 38 | fromPartial: { 39 | execute(value: MsgExecute) { 40 | return { 41 | typeUrl: "/initia.move.v1.MsgExecute", 42 | value: MsgExecute.fromPartial(value) 43 | }; 44 | } 45 | } 46 | }; -------------------------------------------------------------------------------- /packages/client/src/codegen/json-safe.ts: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | /** 3 | * This file and any referenced files were automatically generated by @cosmology/telescope@1.8.3 4 | * DO NOT MODIFY BY HAND. Instead, download the latest proto files for your chain 5 | * and run the transpile command or npm scripts command that is used to regenerate this bundle. 6 | */ 7 | 8 | 9 | export type JsonSafe = { 10 | [Prop in keyof T]: T[Prop] extends Uint8Array | bigint | Date ? string : T[Prop]; 11 | } 12 | -------------------------------------------------------------------------------- /packages/client/src/codegen/opinit/ophost/v1/tx.amino.ts: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | import { MsgInitiateTokenDeposit } from "./tx"; 3 | export const AminoConverter = { 4 | "/opinit.ophost.v1.MsgInitiateTokenDeposit": { 5 | aminoType: "ophost/MsgInitiateTokenDeposit", 6 | toAmino: MsgInitiateTokenDeposit.toAmino, 7 | fromAmino: MsgInitiateTokenDeposit.fromAmino 8 | } 9 | }; -------------------------------------------------------------------------------- /packages/client/src/codegen/opinit/ophost/v1/tx.registry.ts: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | import { GeneratedType } from "@cosmjs/proto-signing"; 3 | import { MsgInitiateTokenDeposit } from "./tx"; 4 | export const registry: ReadonlyArray<[string, GeneratedType]> = [["/opinit.ophost.v1.MsgInitiateTokenDeposit", MsgInitiateTokenDeposit]]; 5 | export const MessageComposer = { 6 | encoded: { 7 | initiateTokenDeposit(value: MsgInitiateTokenDeposit) { 8 | return { 9 | typeUrl: "/opinit.ophost.v1.MsgInitiateTokenDeposit", 10 | value: MsgInitiateTokenDeposit.encode(value).finish() 11 | }; 12 | } 13 | }, 14 | withTypeUrl: { 15 | initiateTokenDeposit(value: MsgInitiateTokenDeposit) { 16 | return { 17 | typeUrl: "/opinit.ophost.v1.MsgInitiateTokenDeposit", 18 | value 19 | }; 20 | } 21 | }, 22 | toJSON: { 23 | initiateTokenDeposit(value: MsgInitiateTokenDeposit) { 24 | return { 25 | typeUrl: "/opinit.ophost.v1.MsgInitiateTokenDeposit", 26 | value: MsgInitiateTokenDeposit.toJSON(value) 27 | }; 28 | } 29 | }, 30 | fromJSON: { 31 | initiateTokenDeposit(value: any) { 32 | return { 33 | typeUrl: "/opinit.ophost.v1.MsgInitiateTokenDeposit", 34 | value: MsgInitiateTokenDeposit.fromJSON(value) 35 | }; 36 | } 37 | }, 38 | fromPartial: { 39 | initiateTokenDeposit(value: MsgInitiateTokenDeposit) { 40 | return { 41 | typeUrl: "/opinit.ophost.v1.MsgInitiateTokenDeposit", 42 | value: MsgInitiateTokenDeposit.fromPartial(value) 43 | }; 44 | } 45 | } 46 | }; -------------------------------------------------------------------------------- /packages/client/src/constants/constants.ts: -------------------------------------------------------------------------------- 1 | export const DEFAULT_GAS_DENOM_OVERRIDES: Record = 2 | /* #__PURE__ */ { 3 | "noble-1": "uusdc", 4 | }; 5 | 6 | export const GAS_STATION_CHAIN_IDS = ["bbn-test-5", "bbn-1"]; 7 | 8 | export const SKIP_API_URL = "https://api.skip.build"; -------------------------------------------------------------------------------- /packages/client/src/injective/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | createTransactionWithSigners, 3 | CreateTransactionArgs as CreateTransactionArgsInjective, 4 | } from "@injectivelabs/sdk-ts"; 5 | import { DEFAULT_STD_FEE } from "@injectivelabs/utils"; 6 | 7 | export type CreateTransactionArgs = { 8 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 9 | message: any | any[]; // the message that should be packed into the transaction 10 | } & CreateTransactionArgsInjective 11 | 12 | export function createTransaction({ 13 | chainId, 14 | message, 15 | timeoutHeight, 16 | memo = "", 17 | fee = DEFAULT_STD_FEE, 18 | signMode = 1, 19 | pubKey, 20 | accountNumber, 21 | sequence, 22 | }: CreateTransactionArgs) { 23 | const signers = { 24 | pubKey: pubKey, 25 | accountNumber: accountNumber, 26 | sequence: sequence, 27 | }; 28 | 29 | return createTransactionWithSigners({ 30 | fee, 31 | memo, 32 | message, 33 | signers, 34 | chainId, 35 | signMode, 36 | timeoutHeight, 37 | }); 38 | } 39 | -------------------------------------------------------------------------------- /packages/client/src/private-functions/getMainnetAndTestnetAssets.ts: -------------------------------------------------------------------------------- 1 | import type { SkipApiOptions } from "src/state/apiState"; 2 | import { assets } from "../api/getAssets"; 3 | import { ClientState } from "../state/clientState"; 4 | 5 | export type GetMainnetAndTestnetAssetsProps = { 6 | chainId?: string; 7 | } & SkipApiOptions; 8 | 9 | export const getMainnetAndTestnetAssets = async ({ 10 | chainId, 11 | ...apiOptions 12 | }: GetMainnetAndTestnetAssetsProps) => { 13 | const [assetsMainnet, assetsTestnet] = await Promise.all([ 14 | assets({ 15 | chainIds: chainId ? [chainId] : undefined, 16 | ...apiOptions, 17 | }), 18 | assets({ 19 | chainIds: chainId ? [chainId] : undefined, 20 | onlyTestnets: true, 21 | ...apiOptions, 22 | }), 23 | ]); 24 | 25 | const mainnet = assetsMainnet; 26 | const testnet = assetsTestnet; 27 | 28 | const merged = { 29 | ...mainnet, 30 | ...testnet, 31 | }; 32 | 33 | ClientState.skipAssets = merged; 34 | 35 | return merged; 36 | }; 37 | -------------------------------------------------------------------------------- /packages/client/src/private-functions/getMainnetAndTestnetChains.ts: -------------------------------------------------------------------------------- 1 | import type { SkipApiOptions } from "src/state/apiState"; 2 | import { chains } from "../api/getChains"; 3 | import { ClientState } from "../state/clientState"; 4 | 5 | export const getMainnetAndTestnetChains = async (apiOptions?: SkipApiOptions) => { 6 | const [mainnetRes, testnetRes] = await Promise.all([ 7 | chains({ 8 | includeEvm: true, 9 | includeSvm: true, 10 | ...apiOptions, 11 | }), 12 | chains({ 13 | includeEvm: true, 14 | includeSvm: true, 15 | onlyTestnets: true, 16 | ...apiOptions, 17 | }), 18 | ]); 19 | 20 | const combinedChains = [...(mainnetRes ?? []), ...(testnetRes ?? [])]; 21 | ClientState.skipChains = combinedChains; 22 | 23 | return combinedChains; 24 | }; 25 | -------------------------------------------------------------------------------- /packages/client/src/private-functions/getRestEndpointForChain.ts: -------------------------------------------------------------------------------- 1 | import { chains, findFirstWorkingEndpoint } from "src/chains"; 2 | import { ClientState } from "../state/clientState"; 3 | 4 | export const getRestEndpointForChain = async (chainId: string) => { 5 | if (ClientState.endpointOptions.getRestEndpointForChain) { 6 | return ClientState.endpointOptions.getRestEndpointForChain(chainId); 7 | } 8 | 9 | if (ClientState.endpointOptions.endpoints && ClientState.endpointOptions.endpoints[chainId]) { 10 | const endpointOptions = ClientState.endpointOptions.endpoints[chainId]; 11 | 12 | if (endpointOptions?.rest) { 13 | return endpointOptions.rest; 14 | } 15 | } 16 | 17 | const chain = chains().find((chain) => chain.chain_id === chainId); 18 | if (!chain) { 19 | throw new Error( 20 | `getRestEndpointForChain error: failed to find chain id '${chainId}' in registry`, 21 | ); 22 | } 23 | if (chain.apis?.rest?.length === 0 || !chain.apis?.rest) { 24 | throw new Error( 25 | `getRestEndpointForChain error: failed to find REST endpoint for chain id '${chainId}'`, 26 | ); 27 | } 28 | const endpoints = chain.apis?.rest?.map((api) => api.address); 29 | const endpoint = await findFirstWorkingEndpoint(endpoints, "rest"); 30 | 31 | if (!endpoint) { 32 | throw new Error( 33 | `getRestEndpointForChain error: failed to find REST endpoint for chain id '${chainId}'`, 34 | ); 35 | } 36 | 37 | return endpoint; 38 | }; 39 | -------------------------------------------------------------------------------- /packages/client/src/private-functions/getRpcEndpointForChain.ts: -------------------------------------------------------------------------------- 1 | import { chains, findFirstWorkingEndpoint } from "src/chains"; 2 | import { ClientState } from "../state/clientState"; 3 | 4 | export const getRpcEndpointForChain = async (chainId: string) => { 5 | if (ClientState.endpointOptions.getRpcEndpointForChain) { 6 | return ClientState.endpointOptions.getRpcEndpointForChain(chainId); 7 | } 8 | 9 | if (ClientState.endpointOptions.endpoints && ClientState.endpointOptions.endpoints[chainId]) { 10 | const endpointOptions = ClientState.endpointOptions.endpoints[chainId]; 11 | 12 | if (endpointOptions?.rpc) { 13 | return endpointOptions.rpc; 14 | } 15 | } 16 | 17 | console.warn( 18 | "Warning: You are using unreliable public endpoints. We strongly recommend overriding them via endpointOptions for use beyond development settings.", 19 | ); 20 | 21 | const chain = chains().find((chain) => chain.chain_id === chainId); 22 | if (!chain) { 23 | throw new Error(`getRpcEndpointForChain: failed to find chain id '${chainId}' in registry`); 24 | } 25 | 26 | if (chain.apis?.rpc?.length === 0 || !chain.apis?.rpc) { 27 | throw new Error( 28 | `getRpcEndpointForChain error: failed to find RPC endpoint for chain id '${chainId}'`, 29 | ); 30 | } 31 | const endpoints = chain.apis?.rpc?.map((api) => api.address); 32 | const endpoint = await findFirstWorkingEndpoint(endpoints, "rpc"); 33 | 34 | if (!endpoint) { 35 | throw new Error( 36 | `getRpcEndpointForChain error: failed to find RPC endpoint for chain id '${chainId}'`, 37 | ); 38 | } 39 | 40 | return endpoint; 41 | }; 42 | -------------------------------------------------------------------------------- /packages/client/src/proto-signing/README.md: -------------------------------------------------------------------------------- 1 | # proto-signing 2 | 3 | https://github.com/archmage-live/archmage-x/tree/develop/lib/network/cosm 4 | -------------------------------------------------------------------------------- /packages/client/src/proto-signing/signer.ts: -------------------------------------------------------------------------------- 1 | // https://github.com/archmage-live/archmage-x/blob/develop/lib/network/cosm/proto-signing/signer.ts 2 | 3 | import type { Algo as CosmAlgo } from "@cosmjs/proto-signing"; 4 | 5 | export type Algo = "eth_secp256k1" | "ethsecp256k1" | CosmAlgo; 6 | 7 | export interface AccountData { 8 | /** A printable address (typically bech32 encoded) */ 9 | readonly address: string; 10 | readonly algo: Algo; 11 | readonly pubkey: Uint8Array; 12 | } 13 | -------------------------------------------------------------------------------- /packages/client/src/public-functions/getCosmosGasAmountForMessage.ts: -------------------------------------------------------------------------------- 1 | import type { EncodeObject } from "@cosmjs/proto-signing"; 2 | import { SigningStargateClient } from "@cosmjs/stargate"; 3 | import type { CosmosMsg } from "src/types/swaggerTypes"; 4 | import { getEncodeObjectFromCosmosMessage } from "../private-functions/cosmos/getEncodeObjectFromCosmosMessage"; 5 | 6 | export const DEFAULT_GAS_MULTIPLIER = 1.5; 7 | 8 | export async function getCosmosGasAmountForMessage( 9 | client: SigningStargateClient, 10 | signerAddress: string, 11 | chainId: string, 12 | messages?: CosmosMsg[], 13 | encodedMsgs?: EncodeObject[], 14 | multiplier: number = DEFAULT_GAS_MULTIPLIER, 15 | ) { 16 | if (!messages && !encodedMsgs) { 17 | throw new Error("Either message or encodedMsg must be provided"); 18 | } 19 | const _encodedMsgs = messages?.map((message) => getEncodeObjectFromCosmosMessage(message)); 20 | encodedMsgs = encodedMsgs || _encodedMsgs; 21 | 22 | if (!encodedMsgs) { 23 | throw new Error("Either message or encodedMsg must be provided"); 24 | } 25 | if ( 26 | chainId.includes("evmos") || 27 | chainId.includes("injective") || 28 | chainId.includes("dymension") || 29 | process?.env.NODE_ENV === "test" 30 | ) { 31 | if (messages?.find((i) => i.msgTypeUrl === "/cosmwasm.wasm.v1.MsgExecuteContract")) { 32 | return "2400000"; 33 | } 34 | return "280000"; 35 | } 36 | 37 | const estimatedGas = await client.simulate(signerAddress, encodedMsgs, ""); 38 | 39 | const estimatedGasWithBuffer = estimatedGas * multiplier; 40 | 41 | return Math.ceil(estimatedGasWithBuffer).toFixed(0); 42 | } 43 | -------------------------------------------------------------------------------- /packages/client/src/public-functions/getEvmGasAmountForMessage.ts: -------------------------------------------------------------------------------- 1 | import type { GetFallbackGasAmount } from "src/types/client-types"; 2 | import { ChainType } from "src/types/swaggerTypes"; 3 | import type { EvmTx } from "src/types/swaggerTypes"; 4 | import { publicActions } from "viem"; 5 | import type { WalletClient } from "viem"; 6 | 7 | export async function getEVMGasAmountForMessage( 8 | signer: WalletClient, 9 | tx: EvmTx, 10 | getFallbackGasAmount?: GetFallbackGasAmount, 11 | ) { 12 | const { to, data, value } = tx; 13 | if (!signer.account) throw new Error("estimateGasForEvmTx: No account found"); 14 | const extendedSigner = signer.extend(publicActions); 15 | 16 | const fee = await extendedSigner.estimateFeesPerGas(); 17 | try { 18 | const gasAmount = await extendedSigner.estimateGas({ 19 | account: signer.account, 20 | to: to as `0x${string}`, 21 | data: `0x${data}`, 22 | value: value === "" ? undefined : BigInt(value ?? ""), 23 | }); 24 | 25 | return gasAmount * fee.maxFeePerGas; 26 | } catch (error) { 27 | const fallbackGasAmount = await getFallbackGasAmount?.(tx.chainId ?? "", ChainType.Evm); 28 | if (fallbackGasAmount) { 29 | return BigInt(fallbackGasAmount) * fee.maxFeePerGas; 30 | } 31 | throw error; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/client/src/public-functions/getRecommendedGasPrice.ts: -------------------------------------------------------------------------------- 1 | import { Decimal } from "@cosmjs/math"; 2 | import { GasPrice } from "@cosmjs/stargate"; 3 | import { getFeeInfoForChain } from "src/public-functions/getFeeInfoForChain"; 4 | import { BigNumber } from "bignumber.js"; 5 | import type { SkipApiOptions } from "src/state/apiState"; 6 | 7 | export type getRecommendedGasPriceProps = { 8 | chainId: string; 9 | } & SkipApiOptions; 10 | 11 | export const getRecommendedGasPrice = async (props: getRecommendedGasPriceProps) => { 12 | const feeInfo = await getFeeInfoForChain(props); 13 | 14 | if (!feeInfo || !feeInfo.gasPrice) { 15 | return undefined; 16 | } 17 | 18 | let price = feeInfo.gasPrice.average; 19 | if (price === "") { 20 | price = feeInfo.gasPrice.high; 21 | } 22 | if (price === "") { 23 | price = feeInfo.gasPrice.low; 24 | } 25 | 26 | if (!price) return; 27 | 28 | return new GasPrice(Decimal.fromUserInput(BigNumber(price).toFixed(), 18), feeInfo.denom); 29 | }; 30 | -------------------------------------------------------------------------------- /packages/client/src/public-functions/getSigningStargateClient.ts: -------------------------------------------------------------------------------- 1 | import type { OfflineSigner } from "@cosmjs/proto-signing"; 2 | import { ClientState } from "../state/clientState"; 3 | import { SigningStargateClient } from "@cosmjs/stargate"; 4 | import { accountParser } from "src/registry"; 5 | import { getRpcEndpointForChain } from "../private-functions/getRpcEndpointForChain"; 6 | 7 | export type getSigningStargateClientProps = { 8 | chainId: string; 9 | getOfflineSigner?: (chainId: string) => Promise; 10 | }; 11 | 12 | export const getSigningStargateClient = async ({ 13 | chainId, 14 | getOfflineSigner, 15 | }: getSigningStargateClientProps) => { 16 | if (!getOfflineSigner) { 17 | throw new Error("'getCosmosSigner' is not provided or configured in skip router"); 18 | } 19 | if (!ClientState.signingStargateClientByChainId?.[chainId]) { 20 | const [signer, endpoint] = await Promise.all([ 21 | getOfflineSigner(chainId), 22 | getRpcEndpointForChain(chainId), 23 | ]); 24 | ClientState.signingStargateClientByChainId[chainId] = 25 | await SigningStargateClient.connectWithSigner(endpoint, signer, { 26 | aminoTypes: ClientState.aminoTypes, 27 | registry: ClientState.registry, 28 | accountParser, 29 | }); 30 | } 31 | 32 | return { 33 | stargateClient: ClientState.signingStargateClientByChainId[chainId] as SigningStargateClient, 34 | signer: await getOfflineSigner(chainId), 35 | }; 36 | }; 37 | -------------------------------------------------------------------------------- /packages/client/src/public-functions/waitForTransaction.ts: -------------------------------------------------------------------------------- 1 | import { trackTransaction, type TrackTxRequest } from "../api/postTrackTransaction"; 2 | import { transactionStatus } from "../api/postTransactionStatus"; 3 | import type { TransactionCallbacks } from "../types/callbacks"; 4 | import { wait } from "../utils/timer"; 5 | 6 | export type WaitForTransactionProps = TrackTxRequest & { 7 | onTransactionTracked?: TransactionCallbacks["onTransactionTracked"]; 8 | }; 9 | 10 | export const waitForTransaction = async ({ 11 | chainId, 12 | txHash, 13 | onTransactionTracked, 14 | ...trackTxPollingOptions 15 | }: WaitForTransactionProps) => { 16 | const { explorerLink } = await trackTransaction({ 17 | chainId, 18 | txHash, 19 | ...trackTxPollingOptions, 20 | }); 21 | await onTransactionTracked?.({ txHash, chainId, explorerLink }); 22 | 23 | // eslint-disable-next-line no-constant-condition 24 | while (true) { 25 | const txStatusResponse = await transactionStatus({ 26 | chainId, 27 | txHash, 28 | }); 29 | 30 | if (txStatusResponse.state === "STATE_COMPLETED_SUCCESS") { 31 | return txStatusResponse; 32 | } 33 | if (txStatusResponse.state === "STATE_COMPLETED_ERROR") { 34 | throw new Error(`${txStatusResponse.error?.type}: ${txStatusResponse.error?.message}`); 35 | } 36 | if (txStatusResponse.state === "STATE_ABANDONED") { 37 | throw new Error("Tracking for the transaction has been abandoned"); 38 | } 39 | 40 | await wait(1000); 41 | } 42 | }; 43 | -------------------------------------------------------------------------------- /packages/client/src/state/apiState.ts: -------------------------------------------------------------------------------- 1 | import type { ChainAffiliates } from "src/types/swaggerTypes"; 2 | import { createRequestClient } from "../utils/generateApi"; 3 | 4 | // eslint-disable-next-line @typescript-eslint/no-extraneous-class 5 | export class ApiState { 6 | static client: ReturnType; 7 | 8 | static chainIdsToAffiliates?: Record; 9 | static cumulativeAffiliateFeeBPS?: string = "0"; 10 | 11 | static isInitialized = false; 12 | static resolveInitialization: () => void; 13 | static clientInitialized: Promise = new Promise((resolve) => { 14 | ApiState.resolveInitialization = () => { 15 | if (!ApiState.isInitialized) { 16 | ApiState.isInitialized = true; 17 | resolve(); 18 | } 19 | }; 20 | }); 21 | 22 | static setClientInitialized() { 23 | ApiState.resolveInitialization(); 24 | } 25 | } 26 | 27 | export type SkipApiOptions = { 28 | apiUrl?: string; 29 | apiKey?: string; 30 | apiHeaders?: HeadersInit; 31 | }; 32 | -------------------------------------------------------------------------------- /packages/client/src/types/callbacks.ts: -------------------------------------------------------------------------------- 1 | import type { Erc20Approval, TransferStatus } from "./swaggerTypes"; 2 | 3 | type CallbackStatus = "error" | "pending" | "completed"; 4 | 5 | export type TransactionCallbacks = { 6 | onTransactionSigned?: (txInfo: { chainId: string }) => Promise; 7 | onTransactionBroadcast?: (txInfo: { txHash: string; chainId: string }) => Promise; 8 | onTransactionTracked?: (txInfo: { 9 | txHash: string; 10 | chainId: string; 11 | explorerLink: string; 12 | }) => Promise; 13 | onTransactionCompleted?: (txInfo: { 14 | chainId: string; 15 | txHash: string; 16 | status?: TransferStatus; 17 | }) => Promise; 18 | onValidateGasBalance?: (value: { 19 | chainId?: string; 20 | txIndex?: number; 21 | status: CallbackStatus; 22 | }) => Promise; 23 | onApproveAllowance?: (value: { 24 | allowance?: Erc20Approval; 25 | status: CallbackStatus; 26 | }) => Promise; 27 | }; 28 | -------------------------------------------------------------------------------- /packages/client/src/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./swaggerTypes"; 2 | export * from "./callbacks"; 3 | export * from "./client-types"; 4 | export * from "./swaggerTypesJson"; 5 | -------------------------------------------------------------------------------- /packages/client/src/utils/filterMessagesRequest.ts: -------------------------------------------------------------------------------- 1 | import type { MessagesRequest } from "src/api/postMessages"; 2 | 3 | export const filterMessagesRequest = (request: MessagesRequest) => { 4 | const messagesRequest = { 5 | sourceAssetDenom: request.sourceAssetDenom, 6 | sourceAssetChainId: request.sourceAssetChainId, 7 | destAssetDenom: request.destAssetDenom, 8 | destAssetChainId: request.destAssetChainId, 9 | amountIn: request.amountIn, 10 | amountOut: request.amountOut, 11 | addressList: request.addressList, 12 | operations: request.operations, 13 | estimatedAmountOut: request.estimatedAmountOut, 14 | slippageTolerancePercent: request.slippageTolerancePercent, 15 | timeoutSeconds: request.timeoutSeconds, 16 | postRouteHandler: request.postRouteHandler, 17 | chainIdsToAffiliates: request.chainIdsToAffiliates, 18 | enableGasWarnings: request.enableGasWarnings, 19 | apiUrl: request.apiUrl, 20 | apiKey: request.apiKey, 21 | abortDuplicateRequests: request.abortDuplicateRequests, 22 | }; 23 | 24 | return messagesRequest; 25 | } -------------------------------------------------------------------------------- /packages/client/src/utils/timer.ts: -------------------------------------------------------------------------------- 1 | export const wait = (ms: number) => { 2 | return new Promise((resolve) => setTimeout(resolve, ms)); 3 | }; 4 | -------------------------------------------------------------------------------- /packages/client/starship/local.yaml: -------------------------------------------------------------------------------- 1 | chains: 2 | - name: osmosis-1 3 | type: osmosis 4 | numValidators: 1 5 | faucet: 6 | enabled: true 7 | ports: 8 | rest: 1313 9 | rpc: 26653 10 | faucet: 8082 11 | 12 | - name: gaia-1 13 | type: cosmos 14 | numValidators: 1 15 | faucet: 16 | enabled: true 17 | ports: 18 | rest: 1317 19 | rpc: 26657 20 | faucet: 8083 21 | 22 | - name: evmos_9000-1 23 | type: evmos 24 | numValidators: 1 25 | faucet: 26 | enabled: false 27 | ports: 28 | rest: 1318 29 | rpc: 26658 30 | 31 | - name: injective-1 32 | type: injective 33 | numValidators: 1 34 | faucet: 35 | enabled: false 36 | ports: 37 | rest: 1319 38 | rpc: 26659 39 | 40 | 41 | relayers: 42 | - name: osmos-gaia 43 | type: hermes 44 | replicas: 1 45 | chains: 46 | - osmosis-1 47 | - gaia-1 48 | - name: injective-osmos 49 | type: hermes 50 | replicas: 1 51 | chains: 52 | - injective-1 53 | - osmosis-1 54 | - name: evmos-osmos 55 | type: hermes 56 | replicas: 1 57 | chains: 58 | - evmos_9000-1 59 | - osmosis-1 60 | 61 | explorer: 62 | enabled: false 63 | 64 | registry: 65 | enabled: true 66 | ports: 67 | rest: 8081 68 | grpc: 9091 69 | -------------------------------------------------------------------------------- /packages/client/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["node_modules", "src/**/*.test.ts", "src/**/*.test.js"], 4 | "include": ["src/**/*.ts", "src/**/*.js"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "baseUrl": ".", 5 | "esModuleInterop": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "isolatedModules": true, 8 | "jsx": "preserve", 9 | "lib": ["dom", "dom.iterable", "esnext"], 10 | "module": "ESNext", 11 | "target": "ESNext", 12 | "moduleResolution": "bundler", 13 | "allowImportingTsExtensions": true, 14 | "verbatimModuleSyntax": true, 15 | "outDir": "./dist", 16 | "noEmit": true, 17 | "noFallthroughCasesInSwitch": true, 18 | "noUncheckedIndexedAccess": true, 19 | "resolveJsonModule": true, 20 | "skipLibCheck": true, 21 | "strict": true, 22 | "strictNullChecks": true 23 | }, 24 | "exclude": ["node_modules", "src/codegen/**/*.ts"], 25 | "include": ["**/*.ts", "**/*.js", "scripts/prepublish.cjs", "scripts/codegen.cjs"] 26 | } 27 | -------------------------------------------------------------------------------- /packages/client/vitest.config.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitest/config"; 2 | 3 | export default defineConfig({ 4 | test: { 5 | threads: false, 6 | deps: { 7 | optimizer: { 8 | web: { 9 | enabled: true, 10 | }, 11 | ssr: { 12 | enabled: true, 13 | }, 14 | }, 15 | }, 16 | globals: true, 17 | }, 18 | }); 19 | -------------------------------------------------------------------------------- /packages/client/vitest.e2e.config.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitest/config"; 2 | 3 | import config from "./vitest.config.mjs"; 4 | 5 | export default defineConfig({ 6 | ...config, 7 | test: { 8 | ...config.test, 9 | include: ["**/e2e/*.test.ts"], 10 | }, 11 | }); 12 | -------------------------------------------------------------------------------- /packages/client/vitest.unit.config.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitest/config"; 2 | 3 | import config from "./vitest.config.mjs"; 4 | 5 | export default defineConfig({ 6 | ...config, 7 | test: { 8 | ...config.test, 9 | exclude: ["**/e2e/**"], 10 | }, 11 | }); 12 | -------------------------------------------------------------------------------- /packages/widget/.env.example: -------------------------------------------------------------------------------- 1 | WORD_PHRASE_KEY="seed phrase" -------------------------------------------------------------------------------- /packages/widget/.storybook/main.ts: -------------------------------------------------------------------------------- 1 | import type { StorybookConfig } from "@storybook/react-vite"; 2 | 3 | import { join, dirname } from "path"; 4 | 5 | /** 6 | * This function is used to resolve the absolute path of a package. 7 | * It is needed in projects that use Yarn PnP or are set up within a monorepo. 8 | */ 9 | function getAbsolutePath(value: string): string { 10 | return dirname(require.resolve(join(value, "package.json"))); 11 | } 12 | const config: StorybookConfig = { 13 | stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"], 14 | addons: [ 15 | getAbsolutePath("@storybook/addon-onboarding"), 16 | getAbsolutePath("@storybook/addon-links"), 17 | getAbsolutePath("@storybook/addon-essentials"), 18 | getAbsolutePath("@chromatic-com/storybook"), 19 | getAbsolutePath("@storybook/addon-interactions"), 20 | ], 21 | framework: { 22 | name: getAbsolutePath("@storybook/react-vite"), 23 | options: {}, 24 | }, 25 | }; 26 | export default config; 27 | -------------------------------------------------------------------------------- /packages/widget/.storybook/preview.tsx: -------------------------------------------------------------------------------- 1 | import { Preview } from "@storybook/react"; 2 | import React from "react"; 3 | import { ShadowDomAndProviders } from "../src/widget/ShadowDomAndProviders"; 4 | 5 | const preview: Preview = { 6 | decorators: [ 7 | (Story) => ( 8 |
9 | 10 | 11 | 12 |
13 | ), 14 | ], 15 | parameters: { 16 | backgrounds: { 17 | default: "gray", 18 | values: [ 19 | { 20 | name: "gray", 21 | value: "gray", 22 | }, 23 | { 24 | name: "white", 25 | value: "white", 26 | }, 27 | { 28 | name: "black", 29 | value: "black", 30 | }, 31 | ], 32 | }, 33 | }, 34 | }; 35 | 36 | export default preview; 37 | -------------------------------------------------------------------------------- /packages/widget/README.md: -------------------------------------------------------------------------------- 1 | # Skip Go Widget 2 | 3 | We document everything about Skip Go Widget in [docs.skip.build/go/widget](https://docs.skip.build/go/widget) 4 | -------------------------------------------------------------------------------- /packages/widget/__tests__/Widget/both-assets-selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skip-mev/skip-go/72247b2ef65a8c6ba340bf069ad06a0bb36dd43c/packages/widget/__tests__/Widget/both-assets-selected.png -------------------------------------------------------------------------------- /packages/widget/__tests__/Widget/connect-keplr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skip-mev/skip-go/72247b2ef65a8c6ba340bf069ad06a0bb36dd43c/packages/widget/__tests__/Widget/connect-keplr.png -------------------------------------------------------------------------------- /packages/widget/__tests__/Widget/default-widget.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skip-mev/skip-go/72247b2ef65a8c6ba340bf069ad06a0bb36dd43c/packages/widget/__tests__/Widget/default-widget.png -------------------------------------------------------------------------------- /packages/widget/__tests__/Widget/usdc-noble-selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skip-mev/skip-go/72247b2ef65a8c6ba340bf069ad06a0bb36dd43c/packages/widget/__tests__/Widget/usdc-noble-selected.png -------------------------------------------------------------------------------- /packages/widget/__tests__/setup/globalSetup.ts: -------------------------------------------------------------------------------- 1 | import dotenv from "dotenv"; 2 | 3 | export default async function globalSetup() { 4 | dotenv.config({ 5 | path: ".env", 6 | override: true, 7 | }); 8 | } 9 | -------------------------------------------------------------------------------- /packages/widget/__tests__/setup/types.ts: -------------------------------------------------------------------------------- 1 | export type Release = { 2 | url: string; 3 | html_url: string; 4 | assets_url: string; 5 | upload_url: string; 6 | tarball_url: string | null; 7 | zipball_url: string | null; 8 | id: number; 9 | node_id: string; 10 | tag_name: string; 11 | target_commitish: string; 12 | name: string | null; 13 | body?: string | null; 14 | draft: boolean; 15 | prerelease: boolean; 16 | created_at: string; 17 | published_at: string | null; 18 | author: SimpleUser; 19 | assets: Asset[]; 20 | body_html?: string; 21 | body_text?: string; 22 | mentions_count?: number; 23 | discussion_url?: string; 24 | }; 25 | 26 | export type SimpleUser = { 27 | name?: string | null; 28 | email?: string | null; 29 | login: string; 30 | id: number; 31 | node_id: string; 32 | avatar_url: string; 33 | gravatar_id: string | null; 34 | url: string; 35 | html_url: string; 36 | followers_url: string; 37 | following_url: string; 38 | gists_url: string; 39 | starred_url: string; 40 | subscriptions_url: string; 41 | organizations_url: string; 42 | repos_url: string; 43 | events_url: string; 44 | received_events_url: string; 45 | type: string; 46 | site_admin: boolean; 47 | starred_at?: string; 48 | }; 49 | 50 | export type Asset = { 51 | url: string; 52 | browser_download_url: string; 53 | id: number; 54 | node_id: string; 55 | name: string; 56 | label: string | null; 57 | state: "uploaded" | "open"; 58 | content_type: string; 59 | size: number; 60 | download_count: number; 61 | created_at: string; 62 | updated_at: string; 63 | uploader: SimpleUser | null; 64 | }; 65 | -------------------------------------------------------------------------------- /packages/widget/__tests__/setup/utils.ts: -------------------------------------------------------------------------------- 1 | import { Page } from "@playwright/test"; 2 | 3 | export async function selectAsset({ 4 | page, 5 | asset, 6 | chain, 7 | }: { 8 | page: Page; 9 | asset: string; 10 | chain: string; 11 | }) { 12 | const selectAsset = page.getByText("Select asset").first(); 13 | await selectAsset.click(); 14 | await page.getByPlaceholder("Search for an asset").fill(asset); 15 | await page 16 | .getByText(asset, { 17 | exact: true, 18 | }) 19 | .click(); 20 | await page.getByPlaceholder("Search networks").fill(chain); 21 | await page 22 | .getByText(chain, { 23 | exact: true, 24 | }) 25 | .click(); 26 | } 27 | 28 | export async function expectPageLoaded(page: Page) { 29 | page.setViewportSize({ 30 | height: 800, 31 | width: 800, 32 | }); 33 | await page.goto("http://localhost:5173/"); 34 | } 35 | -------------------------------------------------------------------------------- /packages/widget/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Widget V2 7 | 8 | 9 |
10 | 12 | 13 | 18 | 19 | -------------------------------------------------------------------------------- /packages/widget/playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, devices } from "@playwright/test"; 2 | 3 | export default defineConfig({ 4 | timeout: 300_000, 5 | retries: 0, 6 | globalSetup: "./__tests__/setup/globalSetup.ts", 7 | projects: [ 8 | { 9 | name: "chromium", 10 | use: { ...devices["Desktop Chrome"] }, 11 | }, 12 | ], 13 | webServer: { 14 | command: "yarn run dev", 15 | url: "http://localhost:5173/", 16 | reuseExistingServer: !process.env.CI, 17 | }, 18 | }); 19 | -------------------------------------------------------------------------------- /packages/widget/scripts/prepublish.cjs: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | const fs = require("fs/promises"); 4 | const path = require("path"); 5 | const packageJson = require("../package.json"); 6 | const coreVersion = require("@skip-go/client/package.json").version; 7 | 8 | async function prepublish() { 9 | delete packageJson.scripts; 10 | delete packageJson.devDependencies; 11 | packageJson.dependencies["@skip-go/client"] = coreVersion; 12 | const targetPath = path.resolve(process.cwd(), "package.json"); 13 | await fs.writeFile(targetPath, JSON.stringify(packageJson, null, 2), { 14 | encoding: "utf-8", 15 | }); 16 | } 17 | 18 | void prepublish(); 19 | -------------------------------------------------------------------------------- /packages/widget/setupTests.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-empty-function */ 2 | global.window.matchMedia = 3 | global.window.matchMedia || 4 | (() => { 5 | return { 6 | matches: false, 7 | addListener: () => {}, 8 | removeListener: () => {}, 9 | addEventListener: () => {}, 10 | removeEventListener: () => {}, 11 | dispatchEvent: () => false, 12 | }; 13 | }); 14 | -------------------------------------------------------------------------------- /packages/widget/src/components/EvmDisclaimer.tsx: -------------------------------------------------------------------------------- 1 | import { skipChainsAtom } from "@/state/skipClient"; 2 | import { useAtomValue } from "jotai"; 3 | import { useMemo } from "react"; 4 | import styled, { useTheme } from "styled-components"; 5 | import { SmallText } from "./Typography"; 6 | import { RouteResponse } from "@skip-go/client"; 7 | 8 | export const EvmDisclaimer = ({ route }: { route?: RouteResponse } = {}) => { 9 | const theme = useTheme(); 10 | const { data: chains } = useAtomValue(skipChainsAtom); 11 | 12 | const usesEvmInOperations = useMemo(() => { 13 | return route?.requiredChainAddresses?.find((chainId) => { 14 | const chainType = chains?.find((chain) => chain.chainId === chainId)?.chainType; 15 | return chainType === "evm"; 16 | }); 17 | }, [chains, route?.requiredChainAddresses]); 18 | 19 | if (usesEvmInOperations) { 20 | return ( 21 | 22 | 23 | This swap contains at least one EVM chain, so it might take longer. 24 |
Read more about common finality times. 25 |
26 |
27 | ); 28 | } 29 | }; 30 | 31 | const StyledEvmWarningMessage = styled.div` 32 | padding: 12px; 33 | border-radius: 5px; 34 | background: ${({ theme }) => theme.warning.background}; 35 | `; 36 | -------------------------------------------------------------------------------- /packages/widget/src/components/GoFastSymbol.tsx: -------------------------------------------------------------------------------- 1 | import { LightningIcon } from "@/icons/LightningIcon"; 2 | import { useTheme } from "styled-components"; 3 | import { Row } from "./Layout"; 4 | import { Tooltip } from "./Tooltip"; 5 | 6 | export const GoFastSymbol = () => { 7 | const theme = useTheme(); 8 | 9 | return ( 10 | 11 | 12 | 13 | 14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /packages/widget/src/components/Layout.tsx: -------------------------------------------------------------------------------- 1 | import { css, styled } from "styled-components"; 2 | 3 | export type FlexProps = { 4 | align?: string; 5 | justify?: string; 6 | gap?: number; 7 | padding?: number; 8 | borderRadius?: number; 9 | flexDirection?: "row" | "column"; 10 | }; 11 | 12 | export const flexProps = css` 13 | display: flex; 14 | ${({ align }) => align && `align-items: ${align}`}; 15 | ${({ justify }) => justify && `justify-content: ${justify}`}; 16 | ${({ gap }) => gap && `gap: ${gap}px`}; 17 | ${({ padding }) => padding && `padding: ${padding}px`}; 18 | ${({ borderRadius }) => borderRadius && `border-radius: ${borderRadius}px`}; 19 | ${({ flexDirection }) => flexDirection && `flex-direction: ${flexDirection}`}; 20 | `; 21 | 22 | export const Row = styled.div` 23 | ${({ width }) => width && `width: ${width}px`}; 24 | ${({ height }) => height && `height: ${height}px`}; 25 | flex-direction: row; 26 | ${flexProps}; 27 | `; 28 | 29 | export const Column = styled(Row)` 30 | flex-direction: column; 31 | `; 32 | 33 | export const Spacer = styled.div` 34 | ${({ width }) => width && `width: ${width}px`}; 35 | ${({ height }) => height && `height: ${height}px`}; 36 | flex-shrink: 0; 37 | flex-grow: 0; 38 | `; 39 | 40 | type SpacerProps = { 41 | width?: number; 42 | height?: number; 43 | }; 44 | -------------------------------------------------------------------------------- /packages/widget/src/components/Skeleton.tsx: -------------------------------------------------------------------------------- 1 | import { css, styled } from "styled-components"; 2 | import { getHexColor, opacityToHex } from "@/utils/colors"; 3 | 4 | export const SkeletonElement = styled.div<{ 5 | width: number; 6 | height: number; 7 | }>` 8 | ${({ width, height, theme }) => css` 9 | width: ${width}px; 10 | height: ${height}px; 11 | background-color: ${getHexColor(theme.primary.text.normal ?? "") + opacityToHex(10)}; 12 | `}; 13 | `; 14 | 15 | export const CircleSkeletonElement = styled(SkeletonElement)` 16 | border-radius: 50%; 17 | `; 18 | -------------------------------------------------------------------------------- /packages/widget/src/constants/skipClientDefault.ts: -------------------------------------------------------------------------------- 1 | const appUrl = "https://go.skip.build"; 2 | 3 | export const endpointOptions = { 4 | getRpcEndpointForChain: async (chainId: string) => { 5 | return `${appUrl}/api/rpc/${chainId}`; 6 | }, 7 | getRestEndpointForChain: async (chainId: string) => { 8 | return `${appUrl}/api/rest/${chainId}`; 9 | }, 10 | }; 11 | 12 | export const prodApiUrl = `${appUrl}/api/skip`; 13 | -------------------------------------------------------------------------------- /packages/widget/src/constants/solana.ts: -------------------------------------------------------------------------------- 1 | import { BackpackWalletAdapter } from "@solana/wallet-adapter-backpack"; 2 | import { WalletAdapterNetwork } from "@solana/wallet-adapter-base"; 3 | import { CoinbaseWalletAdapter } from "@solana/wallet-adapter-coinbase"; 4 | import { LedgerWalletAdapter } from "@solana/wallet-adapter-ledger"; 5 | import { PhantomWalletAdapter } from "@solana/wallet-adapter-phantom"; 6 | import { SolflareWalletAdapter } from "@solana/wallet-adapter-solflare"; 7 | import { TrustWalletAdapter } from "@solana/wallet-adapter-trust"; 8 | import { WalletConnectWalletAdapter } from "@walletconnect/solana-adapter"; 9 | 10 | export const solanaWallets = [ 11 | new PhantomWalletAdapter(), 12 | new BackpackWalletAdapter(), 13 | new SolflareWalletAdapter(), 14 | new CoinbaseWalletAdapter(), 15 | new TrustWalletAdapter(), 16 | new LedgerWalletAdapter(), 17 | new WalletConnectWalletAdapter({ 18 | network: WalletAdapterNetwork.Mainnet, 19 | options: { 20 | projectId: "ff1b9e9bd6329cfb07642bd7f4d11a8c", 21 | }, 22 | }), 23 | ]; 24 | -------------------------------------------------------------------------------- /packages/widget/src/constants/widget.ts: -------------------------------------------------------------------------------- 1 | import { RoutePreference } from "@/state/types"; 2 | 3 | export const SLIPPAGE_OPTIONS = [0.1, 0.5, 1, 3]; 4 | export const DEFAULT_SLIPPAGE = 1; 5 | 6 | export const ROUTE_PREFERENCE_OPTIONS: RoutePreference[] = [ 7 | RoutePreference.FASTEST, 8 | RoutePreference.CHEAPEST, 9 | ]; 10 | export const DEFAULT_DECIMAL_PLACES = 6; 11 | -------------------------------------------------------------------------------- /packages/widget/src/devMode/global.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | min-height: 100vh; 4 | } 5 | 6 | [data-logo="skip-go"] { 7 | display: none !important; 8 | } -------------------------------------------------------------------------------- /packages/widget/src/fonts/ABCDiatype-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skip-mev/skip-go/72247b2ef65a8c6ba340bf069ad06a0bb36dd43c/packages/widget/src/fonts/ABCDiatype-Bold.woff2 -------------------------------------------------------------------------------- /packages/widget/src/fonts/ABCDiatype-Medium.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skip-mev/skip-go/72247b2ef65a8c6ba340bf069ad06a0bb36dd43c/packages/widget/src/fonts/ABCDiatype-Medium.woff2 -------------------------------------------------------------------------------- /packages/widget/src/fonts/ABCDiatype-Regular copy.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skip-mev/skip-go/72247b2ef65a8c6ba340bf069ad06a0bb36dd43c/packages/widget/src/fonts/ABCDiatype-Regular copy.woff2 -------------------------------------------------------------------------------- /packages/widget/src/fonts/ABCDiatype-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skip-mev/skip-go/72247b2ef65a8c6ba340bf069ad06a0bb36dd43c/packages/widget/src/fonts/ABCDiatype-Regular.woff2 -------------------------------------------------------------------------------- /packages/widget/src/fonts/ABCDiatypeMono-Medium.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skip-mev/skip-go/72247b2ef65a8c6ba340bf069ad06a0bb36dd43c/packages/widget/src/fonts/ABCDiatypeMono-Medium.woff2 -------------------------------------------------------------------------------- /packages/widget/src/hooks/useCopyAddress.ts: -------------------------------------------------------------------------------- 1 | import { copyToClipboard } from "@/utils/misc"; 2 | import { useEffect, useState } from "react"; 3 | 4 | export const useCopyAddress = () => { 5 | const [isShowingCopyAddressFeedback, setIsShowingCopyAddressFeedback] = useState(false); 6 | const [copyAddressTimeout, setCopyAddressTimeout] = useState(); 7 | 8 | useEffect(() => { 9 | return () => { 10 | clearTimeout(copyAddressTimeout); 11 | }; 12 | }, [copyAddressTimeout]); 13 | 14 | const copyAddress = (address?: string) => { 15 | if (!address) return; 16 | copyToClipboard(address); 17 | setIsShowingCopyAddressFeedback(true); 18 | const timeout = setTimeout(() => { 19 | setIsShowingCopyAddressFeedback(false); 20 | }, 1000); 21 | setCopyAddressTimeout(timeout); 22 | }; 23 | 24 | return { 25 | copyAddress, 26 | isShowingCopyAddressFeedback, 27 | }; 28 | }; 29 | -------------------------------------------------------------------------------- /packages/widget/src/hooks/useGetBalance.ts: -------------------------------------------------------------------------------- 1 | import { useCallback } from "react"; 2 | import { skipAllBalancesAtom } from "@/state/balances"; 3 | import { useAtomValue } from "jotai"; 4 | 5 | export const useGetBalance = () => { 6 | const { data: skipBalances } = useAtomValue(skipAllBalancesAtom); 7 | 8 | const getBalance = useCallback( 9 | (chainId?: string, denom?: string) => { 10 | if (!chainId || !denom) return; 11 | return skipBalances?.chains?.[chainId]?.denoms?.[denom]; 12 | }, 13 | [skipBalances], 14 | ); 15 | 16 | return getBalance; 17 | }; 18 | -------------------------------------------------------------------------------- /packages/widget/src/hooks/useInjectFontsToDocumentHead.ts: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import regular from "@/fonts/ABCDiatype-Regular.woff2"; 3 | import medium from "@/fonts/ABCDiatype-Medium.woff2"; 4 | import bold from "@/fonts/ABCDiatype-Bold.woff2"; 5 | import monospace from "@/fonts/ABCDiatypeMono-Medium.woff2"; 6 | 7 | export const fonts = ` 8 | @font-face { 9 | font-family: ABCDiatype; 10 | font-weight: 400; 11 | src: url(${regular}) format(woff2); 12 | } 13 | @font-face { 14 | font-family: ABCDiatype; 15 | font-weight: 500; 16 | src: url(${medium}) format(woff2); 17 | } 18 | @font-face { 19 | font-family: ABCDiatype; 20 | font-weight: 700; 21 | src: url(${bold}) format(woff2); 22 | } 23 | @font-face { 24 | font-family: ABCDiatype-mono; 25 | font-weight: 500; 26 | src: url(${monospace}) format(woff2); 27 | } 28 | `; 29 | 30 | export const useInjectFontsToDocumentHead = () => { 31 | useEffect(() => { 32 | const styleElement = document.createElement("style"); 33 | styleElement.textContent = fonts; 34 | 35 | if (!document.head.contains(styleElement)) { 36 | document.head.appendChild(styleElement); 37 | } 38 | 39 | return () => { 40 | document.head.removeChild(styleElement); 41 | }; 42 | }, []); 43 | }; 44 | -------------------------------------------------------------------------------- /packages/widget/src/hooks/useIsGoFast.ts: -------------------------------------------------------------------------------- 1 | import { getClientOperations } from "@/utils/clientType"; 2 | import { OperationType } from "@/utils/clientType"; 3 | import { RouteResponse } from "@skip-go/client"; 4 | import { useMemo } from "react"; 5 | 6 | export const useIsGoFast = (route: RouteResponse | undefined) => { 7 | return useMemo(() => { 8 | const clientOperations = getClientOperations(route?.operations); 9 | return clientOperations?.some((item) => item.type === OperationType.goFastTransfer) ?? false; 10 | }, [route?.operations]); 11 | }; 12 | 13 | export const useIsSwapOperation = (route: RouteResponse | undefined) => { 14 | return useMemo(() => { 15 | const swapOperations = [OperationType.swap, OperationType.evmSwap]; 16 | const clientOperations = getClientOperations(route?.operations); 17 | return clientOperations?.some((item) => swapOperations.includes(item.type)) ?? false; 18 | }, [route?.operations]); 19 | }; 20 | -------------------------------------------------------------------------------- /packages/widget/src/hooks/useIsMobileScreenSize.ts: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | 3 | export const MAX_MOBILE_SCREEN_WIDTH = 767; 4 | 5 | export const useIsMobileScreenSize = () => { 6 | const [isMobileScreenSize, setIsMobileScreenSize] = useState( 7 | typeof window !== "undefined" && window.innerWidth <= MAX_MOBILE_SCREEN_WIDTH, 8 | ); 9 | 10 | useEffect(() => { 11 | if (typeof window === "undefined") return; 12 | const handleResize = () => { 13 | const isMobileScreenSize = window?.innerWidth <= MAX_MOBILE_SCREEN_WIDTH; 14 | setIsMobileScreenSize(isMobileScreenSize); 15 | }; 16 | 17 | handleResize(); 18 | 19 | window?.addEventListener("resize", handleResize); 20 | 21 | return () => { 22 | window?.removeEventListener("resize", handleResize); 23 | }; 24 | }, []); 25 | return isMobileScreenSize; 26 | }; 27 | -------------------------------------------------------------------------------- /packages/widget/src/hooks/useMobileRouteConfig.ts: -------------------------------------------------------------------------------- 1 | import { routeConfigAtom } from "@/state/route"; 2 | import { useSetAtom } from "jotai"; 3 | import { useEffect } from "react"; 4 | import { useIsMobileScreenSize } from "./useIsMobileScreenSize"; 5 | import { WidgetRouteConfig } from "@/widget/Widget"; 6 | 7 | export const useMobileRouteConfig = () => { 8 | const isMobile = useIsMobileScreenSize(); 9 | const setRouteConfig = useSetAtom(routeConfigAtom); 10 | 11 | useEffect(() => { 12 | setRouteConfig((prev: WidgetRouteConfig) => ({ 13 | ...prev, 14 | allowMultiTx: !isMobile, 15 | })); 16 | }, [isMobile, setRouteConfig]); 17 | }; 18 | -------------------------------------------------------------------------------- /packages/widget/src/hooks/usePreventPageUnload.ts: -------------------------------------------------------------------------------- 1 | import { track } from "@amplitude/analytics-browser"; 2 | import { useEffect } from "react"; 3 | 4 | export function usePreventPageUnload(shouldWarn: boolean) { 5 | useEffect(() => { 6 | const handleBeforeUnload = (e: BeforeUnloadEvent) => { 7 | if (shouldWarn) { 8 | track("Prevent user leaving the page before all txs are signed"); 9 | e.preventDefault(); 10 | e.returnValue = 11 | "Please complete the required transaction signatures to complete this trade. Leaving this page before signing both may cause your trade to fail."; // This triggers the dialog in most browsers 12 | return "Please complete the required transaction signatures to complete this trade. Leaving this page before signing both may cause your trade to fail."; // This is for older browsers that still use this return value 13 | } 14 | }; 15 | 16 | window.addEventListener("beforeunload", handleBeforeUnload); 17 | return () => window.removeEventListener("beforeunload", handleBeforeUnload); 18 | }, [shouldWarn]); 19 | } 20 | -------------------------------------------------------------------------------- /packages/widget/src/hooks/usePrimaryChainIdForChainType.ts: -------------------------------------------------------------------------------- 1 | import { onlyTestnetsAtom } from "@/state/skipClient"; 2 | import { ChainType } from "@skip-go/client"; 3 | import { useAtomValue } from "jotai"; 4 | 5 | export const usePrimaryChainIdForChainType = () => { 6 | const onlyTestnets = useAtomValue(onlyTestnetsAtom); 7 | 8 | return onlyTestnets ? chainIdForChainTypeTestnet : chainIdForChainType; 9 | }; 10 | 11 | const chainIdForChainType = { 12 | [ChainType.Cosmos]: "cosmoshub-4", 13 | [ChainType.Evm]: "1", 14 | [ChainType.Svm]: "solana", 15 | }; 16 | 17 | const chainIdForChainTypeTestnet = { 18 | [ChainType.Cosmos]: "provider", 19 | [ChainType.Evm]: "11155111", 20 | [ChainType.Svm]: "solana-devnet", 21 | }; 22 | -------------------------------------------------------------------------------- /packages/widget/src/hooks/useSettingsChanged.ts: -------------------------------------------------------------------------------- 1 | import { useMemo } from "react"; 2 | import { useAtomValue } from "jotai"; 3 | import { swapSettingsAtom, defaultSwapSettings } from "@/state/swapPage"; 4 | 5 | export const useSettingsChanged = () => { 6 | const swapSettings = useAtomValue(swapSettingsAtom); 7 | 8 | return useMemo( 9 | () => 10 | swapSettings.slippage !== defaultSwapSettings.slippage || 11 | swapSettings.routePreference !== defaultSwapSettings.routePreference, 12 | [swapSettings], 13 | ); 14 | }; 15 | -------------------------------------------------------------------------------- /packages/widget/src/hooks/useShowCosmosLedgerWarning.ts: -------------------------------------------------------------------------------- 1 | import { skipChainsAtom } from "@/state/skipClient"; 2 | import { sourceAssetAtom } from "@/state/swapPage"; 3 | import { knownEthermintLikeChains } from "@/state/wallets"; 4 | import { useAtomValue } from "jotai"; 5 | import { useMemo } from "react"; 6 | import { useGetAccount } from "./useGetAccount"; 7 | import { ChainType } from "@skip-go/client"; 8 | 9 | export const useShowCosmosLedgerWarning = () => { 10 | const { data: chains } = useAtomValue(skipChainsAtom); 11 | const sourceAsset = useAtomValue(sourceAssetAtom); 12 | const chainType = chains?.find((c) => c.chainId === sourceAsset?.chainId)?.chainType; 13 | const getAccount = useGetAccount(); 14 | 15 | return useMemo(() => { 16 | if (chainType !== ChainType.Cosmos) return false; 17 | const account = getAccount(sourceAsset?.chainId); 18 | if (!account?.address) return false; 19 | if (!sourceAsset?.chainId) return false; 20 | if (!knownEthermintLikeChains.includes(sourceAsset?.chainId)) return false; 21 | return !!account?.wallet.isLedger; 22 | }, [sourceAsset?.chainId, chainType, getAccount]); 23 | }; 24 | -------------------------------------------------------------------------------- /packages/widget/src/hooks/useSwitchEvmChain.tsx: -------------------------------------------------------------------------------- 1 | import { useAccount } from "wagmi"; 2 | import { useAtomValue } from "jotai"; 3 | import { skipChainsAtom } from "@/state/skipClient"; 4 | import { useCallback } from "react"; 5 | 6 | export const useSwitchEvmChain = () => { 7 | const { connector } = useAccount(); 8 | const { data: chains } = useAtomValue(skipChainsAtom); 9 | 10 | const switchEvmChainId = useCallback( 11 | (chainId?: string) => { 12 | const isEvmChainId = chains?.find((c) => c.chainId === chainId)?.chainType === "evm"; 13 | 14 | if (isEvmChainId) { 15 | connector?.switchChain?.({ 16 | chainId: Number(chainId), 17 | }); 18 | } 19 | }, 20 | [chains, connector], 21 | ); 22 | 23 | return switchEvmChainId; 24 | }; 25 | -------------------------------------------------------------------------------- /packages/widget/src/icons/BridgeArrowIcon.tsx: -------------------------------------------------------------------------------- 1 | type IconProps = { 2 | color?: string; 3 | className?: string; 4 | spin?: boolean; 5 | }; 6 | 7 | export const BridgeArrowIcon = ({ 8 | color = "currentColor", 9 | className, 10 | spin, 11 | ...props 12 | }: IconProps & React.SVGProps) => ( 13 | 22 | 26 | 27 | ); 28 | -------------------------------------------------------------------------------- /packages/widget/src/icons/BridgeIcon.tsx: -------------------------------------------------------------------------------- 1 | type IconProps = { 2 | color?: string; 3 | }; 4 | 5 | export const BridgeIcon = ({ 6 | color = "currentColor", 7 | ...props 8 | }: IconProps & React.SVGProps) => ( 9 | 18 | 24 | 25 | ); 26 | -------------------------------------------------------------------------------- /packages/widget/src/icons/CheckmarkIcon.tsx: -------------------------------------------------------------------------------- 1 | type IconProps = { 2 | color?: string; 3 | }; 4 | 5 | export const CheckmarkIcon = ({ color = "currentColor" }: IconProps) => ( 6 | 7 | 8 | 9 | 10 | 14 | 15 | 16 | 17 | 18 | ); 19 | -------------------------------------------------------------------------------- /packages/widget/src/icons/ChevronIcon.tsx: -------------------------------------------------------------------------------- 1 | type IconProps = { 2 | color?: string; 3 | backgroundColor?: string; 4 | noBackground?: boolean; 5 | }; 6 | 7 | export const ChevronIcon = ({ 8 | color = "currentColor", 9 | backgroundColor = "transparent", 10 | noBackground = false, 11 | ...props 12 | }: IconProps & React.SVGProps) => ( 13 | 21 | {!noBackground && } 22 | 26 | 27 | ); 28 | -------------------------------------------------------------------------------- /packages/widget/src/icons/CopyIcon.tsx: -------------------------------------------------------------------------------- 1 | type IconProps = { 2 | color?: string; 3 | }; 4 | 5 | export const CopyIcon = ({ 6 | color = "currentColor", 7 | ...props 8 | }: IconProps & React.SVGProps) => ( 9 | 17 | 21 | 22 | ); 23 | -------------------------------------------------------------------------------- /packages/widget/src/icons/FilledWarningIcon.tsx: -------------------------------------------------------------------------------- 1 | type IconProps = { 2 | color?: string; 3 | backgroundColor?: string; 4 | }; 5 | 6 | export const FilledWarningIcon = ({ 7 | color = "currentColor", 8 | backgroundColor = "transparent", 9 | }: IconProps) => ( 10 | 11 | 12 | 16 | 17 | ); 18 | -------------------------------------------------------------------------------- /packages/widget/src/icons/GoFastIcon.tsx: -------------------------------------------------------------------------------- 1 | type IconProps = { 2 | backgroundColor?: string; 3 | className?: string; 4 | }; 5 | 6 | export const GoFastIcon = ({ 7 | backgroundColor = "currentColor", 8 | ...props 9 | }: IconProps & React.SVGProps) => ( 10 | 18 | 22 | 23 | ); 24 | -------------------------------------------------------------------------------- /packages/widget/src/icons/HamburgerIcon.tsx: -------------------------------------------------------------------------------- 1 | type IconProps = { 2 | color?: string; 3 | }; 4 | 5 | export const HamburgerIcon = ({ color = "currentColor" }: IconProps) => ( 6 | 7 | 8 | 9 | 10 | 11 | ); 12 | -------------------------------------------------------------------------------- /packages/widget/src/icons/HistoryArrowIcon.tsx: -------------------------------------------------------------------------------- 1 | type IconProps = { 2 | color?: string; 3 | }; 4 | 5 | export const HistoryArrowIcon = ({ 6 | color = "currentColor", 7 | ...props 8 | }: IconProps & React.SVGProps) => ( 9 | 17 | 21 | 22 | ); 23 | -------------------------------------------------------------------------------- /packages/widget/src/icons/HistoryIcon.tsx: -------------------------------------------------------------------------------- 1 | type IconProps = { 2 | color?: string; 3 | }; 4 | 5 | export const HistoryIcon = ({ 6 | color = "currentColor", 7 | ...props 8 | }: IconProps & React.SVGProps) => ( 9 | 17 | 21 | 25 | 26 | ); 27 | -------------------------------------------------------------------------------- /packages/widget/src/icons/HorizontalLineIcon.tsx: -------------------------------------------------------------------------------- 1 | type IconProps = { 2 | color?: string; 3 | }; 4 | 5 | export const HorizontalLineIcon = ({ color = "currentColor" }: IconProps) => ( 6 | 7 | 8 | 9 | ); 10 | -------------------------------------------------------------------------------- /packages/widget/src/icons/LightningIcon.tsx: -------------------------------------------------------------------------------- 1 | type IconProps = { 2 | color?: string; 3 | }; 4 | 5 | export const LightningIcon = ({ 6 | color = "currentColor", 7 | ...props 8 | }: IconProps & React.SVGProps) => ( 9 | 17 | 21 | 22 | ); 23 | -------------------------------------------------------------------------------- /packages/widget/src/icons/PenIcon.tsx: -------------------------------------------------------------------------------- 1 | type IconProps = { 2 | color?: string; 3 | }; 4 | 5 | export const PenIcon = ({ color = "currentColor" }: IconProps) => ( 6 | 7 | 11 | 12 | ); 13 | -------------------------------------------------------------------------------- /packages/widget/src/icons/PlusIcon.tsx: -------------------------------------------------------------------------------- 1 | type IconProps = { 2 | color?: string; 3 | }; 4 | 5 | export const PlusIcon = ({ color = "transparent" }: IconProps) => ( 6 | 7 | 8 | 9 | 10 | 14 | 15 | 16 | 17 | 18 | ); 19 | -------------------------------------------------------------------------------- /packages/widget/src/icons/QuestionMarkIcon.tsx: -------------------------------------------------------------------------------- 1 | type IconProps = { 2 | color?: string; 3 | }; 4 | 5 | export const QuestionMarkIcon = ({ 6 | color = "currentColor", 7 | ...props 8 | }: IconProps & React.SVGProps) => ( 9 | 17 | 23 | 24 | ); 25 | -------------------------------------------------------------------------------- /packages/widget/src/icons/RouteArrow.tsx: -------------------------------------------------------------------------------- 1 | type IconProps = { 2 | color?: string; 3 | }; 4 | 5 | export const RouteArrow = ({ color = "currentColor" }: IconProps) => ( 6 | 7 | 11 | 12 | ); 13 | -------------------------------------------------------------------------------- /packages/widget/src/icons/SearchIcon.tsx: -------------------------------------------------------------------------------- 1 | type IconProps = { 2 | color?: string; 3 | className?: string; 4 | }; 5 | 6 | export const SearchIcon = ({ color = "currentColor", className }: IconProps) => ( 7 | 15 | 19 | 20 | ); 21 | -------------------------------------------------------------------------------- /packages/widget/src/icons/SpinnerIcon.tsx: -------------------------------------------------------------------------------- 1 | import type { ComponentProps } from "react"; 2 | 3 | export const SpinnerIcon = (props: ComponentProps<"svg">) => { 4 | return ( 5 | 6 | 7 | 12 | 13 | ); 14 | }; 15 | -------------------------------------------------------------------------------- /packages/widget/src/icons/SwapExecutionBridgeIcon.tsx: -------------------------------------------------------------------------------- 1 | type IconProps = { 2 | color?: string; 3 | }; 4 | 5 | export const SwapExecutionBridgeIcon = ({ 6 | color = "currentColor", 7 | ...props 8 | }: IconProps & React.SVGProps) => ( 9 | 17 | 21 | 22 | ); 23 | -------------------------------------------------------------------------------- /packages/widget/src/icons/SwapExecutionSendIcon.tsx: -------------------------------------------------------------------------------- 1 | type IconProps = { 2 | color?: string; 3 | }; 4 | 5 | export const SwapExecutionSendIcon = ({ 6 | color = "currentColor", 7 | ...props 8 | }: IconProps & React.SVGProps) => ( 9 | 17 | 21 | 27 | 28 | ); 29 | -------------------------------------------------------------------------------- /packages/widget/src/icons/SwapExecutionSwapIcon.tsx: -------------------------------------------------------------------------------- 1 | type IconProps = { 2 | color?: string; 3 | }; 4 | 5 | export const SwapExecutionSwapIcon = ({ 6 | color = "currentColor", 7 | ...props 8 | }: IconProps & React.SVGProps) => ( 9 | 17 | 21 | 22 | ); 23 | -------------------------------------------------------------------------------- /packages/widget/src/icons/SwapIcon.tsx: -------------------------------------------------------------------------------- 1 | type IconProps = { 2 | color?: string; 3 | }; 4 | 5 | export const SwapIcon = ({ color = "currentColor" }: IconProps) => ( 6 | 7 | 8 | 9 | 10 | 14 | 18 | 19 | 20 | 21 | 22 | ); 23 | -------------------------------------------------------------------------------- /packages/widget/src/icons/ThinArrowIcon.tsx: -------------------------------------------------------------------------------- 1 | type IconProps = { 2 | color?: string; 3 | direction?: "left" | "right"; 4 | }; 5 | 6 | export const ThinArrowIcon = ({ color = "currentColor", direction = "left" }: IconProps) => ( 7 | 15 | 19 | 20 | ); 21 | -------------------------------------------------------------------------------- /packages/widget/src/icons/TinyTriangleIcon.tsx: -------------------------------------------------------------------------------- 1 | type IconProps = { 2 | color?: string; 3 | direction?: "up" | "down"; 4 | }; 5 | 6 | export const TinyTriangleIcon = ({ 7 | color = "currentColor", 8 | direction, 9 | ...props 10 | }: IconProps & React.SVGProps) => ( 11 | 20 | 21 | 22 | ); 23 | -------------------------------------------------------------------------------- /packages/widget/src/icons/TriangleWarningIcon.tsx: -------------------------------------------------------------------------------- 1 | type IconProps = { 2 | backgroundColor?: string; 3 | }; 4 | 5 | export const TriangleWarningIcon = ({ backgroundColor }: IconProps) => ( 6 | 7 | 13 | 14 | ); 15 | -------------------------------------------------------------------------------- /packages/widget/src/icons/WarningIcon.tsx: -------------------------------------------------------------------------------- 1 | type IconProps = { 2 | color?: string; 3 | backgroundColor?: string; 4 | }; 5 | 6 | export const WarningIcon = ({ 7 | color = "currentColor", 8 | backgroundColor = "transparent", 9 | }: IconProps) => ( 10 | 11 | 12 | 16 | 17 | ); 18 | -------------------------------------------------------------------------------- /packages/widget/src/icons/XIcon.tsx: -------------------------------------------------------------------------------- 1 | type IconProps = { 2 | color?: string; 3 | }; 4 | 5 | export const XIcon = ({ 6 | color = "currentColor", 7 | ...props 8 | }: IconProps & React.SVGProps) => ( 9 | 17 | 21 | 22 | ); 23 | -------------------------------------------------------------------------------- /packages/widget/src/index.tsx: -------------------------------------------------------------------------------- 1 | export { Widget } from "./widget/Widget"; 2 | export type { WidgetProps } from "./widget/Widget"; 3 | export { defaultTheme, lightTheme } from "./widget/theme"; 4 | export { resetWidget, setAsset } from "./state/swapPage"; 5 | export type { Theme } from "./widget/theme"; 6 | export { openAssetAndChainSelectorModal } from "./modals/AssetAndChainSelectorModal/AssetAndChainSelectorModal"; 7 | -------------------------------------------------------------------------------- /packages/widget/src/modals/ConnectedWalletModal/ConnectedWalletModal.tsx: -------------------------------------------------------------------------------- 1 | import { createModal, ModalProps } from "@/components/Modal"; 2 | import { 3 | ModalHeader, 4 | StyledModalContainer, 5 | StyledModalInnerContainer, 6 | } from "@/components/ModalHeader"; 7 | import NiceModal from "@ebay/nice-modal-react"; 8 | import { Modals } from "../registerModals"; 9 | import { track } from "@amplitude/analytics-browser"; 10 | import { EcosystemConnectors } from "@/modals/ConnectedWalletModal/EcosystemConnectors"; 11 | 12 | const ITEM_HEIGHT = 60; 13 | const ITEM_GAP = 5; 14 | 15 | export type ConnectedWalletModalProps = ModalProps; 16 | 17 | export const ConnectedWalletModal = createModal(() => { 18 | return ( 19 | 20 | { 23 | track("connect eco modal: header back button - clicked"); 24 | NiceModal.remove(Modals.ConnectedWalletModal); 25 | }} 26 | /> 27 | 28 | 29 | 30 | 31 | ); 32 | }); 33 | -------------------------------------------------------------------------------- /packages/widget/src/modals/ConnectedWalletModal/EcosystemConnectors.tsx: -------------------------------------------------------------------------------- 1 | import { useMemo } from "react"; 2 | import { ConnectEcoRow } from "@/modals/ConnectedWalletModal/ConnectEcoRow"; 3 | import { ChainType } from "@skip-go/client"; 4 | 5 | type EcosystemConnectorsProps = { 6 | excludeChainType?: ChainType; 7 | connectedWalletModal?: boolean; 8 | onClick?: (chainType: ChainType) => void; 9 | }; 10 | 11 | const ALL_ECOSYSTEMS: ChainType[] = [ChainType.Cosmos, ChainType.Evm, ChainType.Svm]; 12 | 13 | export const EcosystemConnectors = ({ 14 | excludeChainType, 15 | onClick, 16 | connectedWalletModal, 17 | }: EcosystemConnectorsProps) => { 18 | const ecosystemsToRender = useMemo(() => { 19 | return ALL_ECOSYSTEMS.filter((eco) => eco !== excludeChainType); 20 | }, [excludeChainType]); 21 | 22 | return ( 23 | <> 24 | {ecosystemsToRender.map((chainType) => ( 25 | onClick?.(chainType)} 29 | connectedWalletModal={connectedWalletModal} 30 | /> 31 | ))} 32 | 33 | ); 34 | }; 35 | -------------------------------------------------------------------------------- /packages/widget/src/modals/SetAddressModal/isValidWalletAddress.ts: -------------------------------------------------------------------------------- 1 | import { isAddress } from "viem"; 2 | import { PublicKey } from "@solana/web3.js"; 3 | import { bech32m, bech32 } from "@/utils/bech32"; 4 | import { ChainType } from "@skip-go/client"; 5 | 6 | type isValidWalletAddressProps = { 7 | address: string; 8 | chainType: ChainType | string; 9 | bech32Prefix: string; 10 | chainId?: string; 11 | }; 12 | 13 | export const isValidWalletAddress = ({ 14 | address, 15 | chainType, 16 | bech32Prefix, 17 | chainId, 18 | }: isValidWalletAddressProps) => { 19 | if (chainId?.includes("penumbra")) { 20 | try { 21 | return bech32Prefix === bech32m.decode(address, 143)?.prefix; 22 | } catch { 23 | // The temporary solution to route around Noble address breakage. 24 | // This can be entirely removed once `noble-1` upgrades. 25 | return ["penumbracompat1", "tpenumbra"].includes(bech32.decode(address).prefix); 26 | } 27 | } 28 | switch (chainType) { 29 | case ChainType.Cosmos: 30 | try { 31 | const { prefix } = bech32.decode(address); 32 | return bech32Prefix === prefix; 33 | } catch (_error) { 34 | return false; 35 | } 36 | case ChainType.Evm: 37 | try { 38 | return isAddress(address); 39 | } catch (_error) { 40 | return false; 41 | } 42 | case ChainType.Svm: 43 | try { 44 | const publicKey = new PublicKey(address); 45 | return PublicKey.isOnCurve(publicKey); 46 | } catch (_error) { 47 | return false; 48 | } 49 | default: 50 | return false; 51 | } 52 | }; 53 | -------------------------------------------------------------------------------- /packages/widget/src/modals/registerModals.ts: -------------------------------------------------------------------------------- 1 | import NiceModal from "@ebay/nice-modal-react"; 2 | import { WalletSelectorModal } from "./WalletSelectorModal/WalletSelectorModal"; 3 | import { ConnectedWalletModal } from "./ConnectedWalletModal/ConnectedWalletModal"; 4 | import { SwapSettingsDrawer } from "./SwapSettingsDrawer/SwapSettingsDrawer"; 5 | import { AssetAndChainSelectorModal } from "./AssetAndChainSelectorModal/AssetAndChainSelectorModal"; 6 | import { SetAddressModal } from "./SetAddressModal/SetAddressModal"; 7 | 8 | export const registerModals = () => { 9 | NiceModal.register(Modals.AssetAndChainSelectorModal, AssetAndChainSelectorModal); 10 | NiceModal.register(Modals.ConnectedWalletModal, ConnectedWalletModal); 11 | NiceModal.register(Modals.SetAddressModal, SetAddressModal); 12 | NiceModal.register(Modals.SwapSettingsDrawer, SwapSettingsDrawer); 13 | NiceModal.register(Modals.WalletSelectorModal, WalletSelectorModal); 14 | }; 15 | 16 | export enum Modals { 17 | AssetAndChainSelectorModal = "AssetAndChainSelectorModal", 18 | ConnectedWalletModal = "ConnectedWalletModal", 19 | SetAddressModal = "SetAddressModal", 20 | SwapSettingsDrawer = "SwapSettingsDrawer", 21 | WalletSelectorModal = "WalletSelectorModal", 22 | } 23 | -------------------------------------------------------------------------------- /packages/widget/src/pages/SwapExecutionPage/useCountdown.ts: -------------------------------------------------------------------------------- 1 | import { createCountdownTimer } from "@/utils/countdownTimer"; 2 | import { useState, useEffect } from "react"; 3 | 4 | export const useCountdown = ({ 5 | estimatedRouteDurationSeconds, 6 | enabled, 7 | }: { 8 | estimatedRouteDurationSeconds?: number; 9 | enabled?: boolean; 10 | }) => { 11 | const [countdown, setCountdown] = useState(estimatedRouteDurationSeconds); 12 | const [timer, setTimer] = useState | undefined>(); 13 | 14 | useEffect(() => { 15 | const estimatedDurationSeconds = estimatedRouteDurationSeconds; 16 | if (!timer && estimatedDurationSeconds && enabled) { 17 | const countdownTimer = createCountdownTimer({ 18 | duration: estimatedDurationSeconds * 1_000, 19 | onUpdate: (remainingTime) => { 20 | setCountdown(parseInt((remainingTime / 1_000).toString())); 21 | }, 22 | }); 23 | setTimer(countdownTimer); 24 | countdownTimer.startCountdown(); 25 | } 26 | 27 | return () => { 28 | timer?.stopCountdown(); 29 | }; 30 | }, [estimatedRouteDurationSeconds, enabled, timer]); 31 | 32 | return countdown; 33 | }; 34 | -------------------------------------------------------------------------------- /packages/widget/src/pages/SwapExecutionPage/useIsGasStationTx.tsx: -------------------------------------------------------------------------------- 1 | import { useAtomValue } from "jotai"; 2 | import { useMemo } from "react"; 3 | import { skipChainsAtom } from "@/state/skipClient"; 4 | import { swapExecutionStateAtom } from "@/state/swapExecutionPage"; 5 | import { GAS_STATION_CHAIN_IDS, ChainType } from "@skip-go/client"; 6 | import { OperationType } from "@/utils/clientType"; 7 | 8 | export const useIsGasStationTx = () => { 9 | const { route } = useAtomValue(swapExecutionStateAtom); 10 | const { data: chains } = useAtomValue(skipChainsAtom); 11 | 12 | const isGasStationTx = useMemo(() => { 13 | if (!chains || !route) return false; 14 | 15 | const sourceChain = chains.find((chain) => chain.chainId === route.sourceAssetChainId); 16 | const destChain = chains.find((chain) => chain.chainId === route.destAssetChainId); 17 | 18 | if (!sourceChain || !destChain) return false; 19 | 20 | const isAxelarTransfer = route.operations.some((op) => OperationType.axelarTransfer in op); 21 | 22 | return ( 23 | sourceChain?.chainType === ChainType.Evm && 24 | !isAxelarTransfer && 25 | GAS_STATION_CHAIN_IDS.includes(destChain?.chainId) 26 | ); 27 | }, [chains, route]); 28 | 29 | return isGasStationTx; 30 | }; 31 | -------------------------------------------------------------------------------- /packages/widget/src/pages/SwapPage/useCleanupDebouncedAtoms.ts: -------------------------------------------------------------------------------- 1 | import { 2 | cleanupDebouncedDestinationAssetAmountAtom, 3 | cleanupDebouncedSourceAssetAmountAtom, 4 | } from "@/state/swapPage"; 5 | 6 | import { useSetAtom } from "jotai"; 7 | import { useEffect } from "react"; 8 | 9 | export const useCleanupDebouncedAtoms = () => { 10 | const cleanupDebouncedSourceAssetAmount = useSetAtom(cleanupDebouncedSourceAssetAmountAtom); 11 | const cleanupDebouncedDestinationAssetAmount = useSetAtom( 12 | cleanupDebouncedDestinationAssetAmountAtom, 13 | ); 14 | 15 | useEffect(() => { 16 | return () => { 17 | cleanupDebouncedSourceAssetAmount(undefined); 18 | cleanupDebouncedDestinationAssetAmount(undefined); 19 | }; 20 | }, [cleanupDebouncedDestinationAssetAmount, cleanupDebouncedSourceAssetAmount]); 21 | }; 22 | -------------------------------------------------------------------------------- /packages/widget/src/providers/CosmosProvider.tsx: -------------------------------------------------------------------------------- 1 | import { mainnetChains, testnetChains } from "@/constants/chains"; 2 | import { onlyTestnetsAtom } from "@/state/skipClient"; 3 | import { walletConnectAtom } from "@/state/wallets"; 4 | import { GrazProvider } from "graz"; 5 | import { useAtomValue } from "jotai"; 6 | 7 | type CosmosProviderProps = { 8 | children: React.ReactNode; 9 | }; 10 | 11 | export const CosmosProvider: React.FC = ({ children }) => { 12 | const isTestnet = useAtomValue(onlyTestnetsAtom); 13 | const walletConnect = useAtomValue(walletConnectAtom); 14 | return ( 15 | 36 | {children} 37 | 38 | ); 39 | }; 40 | -------------------------------------------------------------------------------- /packages/widget/src/providers/EVMProvider.tsx: -------------------------------------------------------------------------------- 1 | import { config } from "@/constants/wagmi"; 2 | import React from "react"; 3 | import { Config, WagmiProvider } from "wagmi"; 4 | 5 | type EVMProviderProps = { 6 | children: React.ReactNode; 7 | wagmiConfig?: Config; 8 | }; 9 | 10 | export const EVMProvider: React.FC = ({ children, wagmiConfig }) => { 11 | return ( 12 | 17 | {children} 18 | 19 | ); 20 | }; 21 | -------------------------------------------------------------------------------- /packages/widget/src/state/assetSymbolsSortedToTop.ts: -------------------------------------------------------------------------------- 1 | import { atom } from "jotai"; 2 | 3 | const PRIVILEGED_ASSETS = ["ATOM", "USDC", "USDT", "ETH", "TIA", "OSMO", "NTRN", "INJ"]; 4 | 5 | export const assetSymbolsSortedToTopAtom = atom(PRIVILEGED_ASSETS); 6 | -------------------------------------------------------------------------------- /packages/widget/src/state/balances.ts: -------------------------------------------------------------------------------- 1 | import { atomWithQuery } from "jotai-tanstack-query"; 2 | import { isInvertingSwapAtom } from "./swapPage"; 3 | import { atom } from "jotai"; 4 | import { BalanceRequest, BalanceResponse, balances } from "@skip-go/client"; 5 | 6 | export const skipAllBalancesRequestAtom = atom(undefined); 7 | 8 | export const skipAllBalancesAtom = atomWithQuery((get) => { 9 | const params = get(skipAllBalancesRequestAtom); 10 | const isInvertingSwap = get(isInvertingSwapAtom); 11 | 12 | const enabled = params && !isInvertingSwap; 13 | 14 | return { 15 | queryKey: ["skipBalances", params], 16 | queryFn: async () => { 17 | if (!params) { 18 | return { chains: {} }; 19 | } 20 | if (Object.keys(params?.chains ?? {}).length === 0) { 21 | return { chains: {} }; 22 | } 23 | return balances({ ...params, abortDuplicateRequests: true }); 24 | }, 25 | enabled, 26 | refetchInterval: 1000 * 60, 27 | retry: 1, 28 | gcTime: 0, 29 | placeholderData: (prevData: BalanceResponse | undefined) => prevData, 30 | }; 31 | }); 32 | -------------------------------------------------------------------------------- /packages/widget/src/state/filters.ts: -------------------------------------------------------------------------------- 1 | import { atom } from "jotai"; 2 | 3 | export type ChainFilter = { 4 | source?: Record; 5 | destination?: Record; 6 | }; 7 | 8 | export const filterAtom = atom(); 9 | 10 | export const filterOutAtom = atom(); 11 | 12 | export const filterOutUnlessUserHasBalanceAtom = atom(); 13 | -------------------------------------------------------------------------------- /packages/widget/src/state/hideAssetsUnlessWalletTypeConnected.ts: -------------------------------------------------------------------------------- 1 | import { atom } from "jotai"; 2 | 3 | export const hideAssetsUnlessWalletTypeConnectedAtom = atom(false); 4 | -------------------------------------------------------------------------------- /packages/widget/src/state/ibcEurekaHighlightedAssets.ts: -------------------------------------------------------------------------------- 1 | import { atom } from "jotai"; 2 | 3 | export type IbcEurekaHighlightedAssets = Record; 4 | 5 | // the key will be recommended symbol and the value is the chainIDs 6 | // if the value is undefined all chains are highlighted 7 | // example { USDC: ['cosmoshub-4'], USDT: undefined } 8 | export const ibcEurekaHighlightedAssetsAtom = atom({}); 9 | -------------------------------------------------------------------------------- /packages/widget/src/state/localStorageKeys.ts: -------------------------------------------------------------------------------- 1 | export enum LOCAL_STORAGE_KEYS { 2 | sourceAsset = "sourceAsset", 3 | destinationAsset = "destinationAsset", 4 | transactionHistory = "transactionHistory", 5 | swapExecutionState = "swapExecutionState", 6 | extraCosmosChainIdsToConnectPerWallet = "extraCosmosChainIdsToConnectPerWallet", 7 | } 8 | -------------------------------------------------------------------------------- /packages/widget/src/state/router.ts: -------------------------------------------------------------------------------- 1 | import { atom } from "jotai"; 2 | 3 | export enum Routes { 4 | SwapPage, 5 | SwapExecutionPage, 6 | TransactionHistoryPage, 7 | } 8 | 9 | export const currentPageAtom = atom(Routes.SwapPage); 10 | -------------------------------------------------------------------------------- /packages/widget/src/state/settingsDrawer.ts: -------------------------------------------------------------------------------- 1 | import { atom } from "jotai"; 2 | 3 | export const settingsDrawerAtom = atom(undefined); 4 | -------------------------------------------------------------------------------- /packages/widget/src/state/types.ts: -------------------------------------------------------------------------------- 1 | export enum RoutePreference { 2 | FASTEST = "Fastest", 3 | CHEAPEST = "Cheapest", 4 | } 5 | -------------------------------------------------------------------------------- /packages/widget/src/stories/AssetChainInput.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from "@storybook/react"; 2 | import { AssetChainInput } from "@/components/AssetChainInput"; 3 | import { renderLightAndDarkTheme } from "./renderLightAndDarkTheme"; 4 | 5 | // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export 6 | const meta = { 7 | title: "Components/AssetChainInput", 8 | component: (props) => renderLightAndDarkTheme(), 9 | parameters: { 10 | // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout 11 | layout: "centered", 12 | }, 13 | // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs 14 | tags: ["autodocs"], 15 | // More on argTypes: https://storybook.js.org/docs/api/argtypes 16 | // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args 17 | args: {}, 18 | } satisfies Meta; 19 | 20 | export default meta; 21 | type Story = StoryObj; 22 | 23 | // More on writing stories with args: https://storybook.js.org/docs/writing-stories/args 24 | 25 | export const Primary: Story = { 26 | args: { 27 | value: "50", 28 | selectedAssetDenom: "uatom", 29 | handleChangeAsset: () => alert("change asset"), 30 | handleChangeChain: () => alert("change chain"), 31 | }, 32 | }; 33 | 34 | export const NoProps: Story = {}; 35 | -------------------------------------------------------------------------------- /packages/widget/src/stories/GhostButton.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from "@storybook/react"; 2 | import { renderLightAndDarkTheme } from "./renderLightAndDarkTheme"; 3 | import { GhostButton } from "@/components/Button"; 4 | 5 | // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export 6 | const meta = { 7 | title: "Components/GhostButton", 8 | component: (props) => renderLightAndDarkTheme(), 9 | parameters: { 10 | // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout 11 | layout: "centered", 12 | }, 13 | // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs 14 | tags: ["autodocs"], 15 | // More on argTypes: https://storybook.js.org/docs/api/argtypes 16 | // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args 17 | } satisfies Meta; 18 | 19 | export default meta; 20 | type Story = StoryObj; 21 | 22 | // More on writing stories with args: https://storybook.js.org/docs/writing-stories/args 23 | 24 | export const primary: Story = { 25 | args: { 26 | onClick: () => alert("handle click"), 27 | children: "ghost button", 28 | }, 29 | }; 30 | -------------------------------------------------------------------------------- /packages/widget/src/stories/SwapPage.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from "@storybook/react"; 2 | import { SwapPage } from "@/pages/SwapPage/SwapPage"; 3 | import { renderLightAndDarkTheme } from "./renderLightAndDarkTheme"; 4 | import { styled } from "styled-components"; 5 | import NiceModal from "@ebay/nice-modal-react"; 6 | 7 | const meta = { 8 | title: "Pages/SwapPage", 9 | component: (props) => 10 | renderLightAndDarkTheme( 11 | 12 | 13 | 14 | 15 | , 16 | undefined, 17 | true, 18 | ), 19 | parameters: { 20 | // More on how to position stories at: https://storybook.js.org/docs/configure/story-layout 21 | layout: "fullscreen", 22 | }, 23 | } satisfies Meta; 24 | type Story = StoryObj; 25 | 26 | export default meta; 27 | 28 | export const Example: Story = { 29 | args: {}, 30 | }; 31 | 32 | const SwapFlowContainer = styled.div` 33 | position: relative; 34 | width: fit-content; 35 | `; 36 | -------------------------------------------------------------------------------- /packages/widget/src/stories/TokenAndChainSelectorModal.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta } from "@storybook/react"; 2 | import NiceModal, { useModal } from "@ebay/nice-modal-react"; 3 | import { TokenAndChainSelectorModal } from "@/modals/TokenAndChainSelectorModal/TokenAndChainSelectorModal"; 4 | import { Row } from "@/components/Layout"; 5 | import { defaultTheme, lightTheme } from "@/widget/theme"; 6 | 7 | const meta = { 8 | title: "Modals/TokenAndChainSelectorModal", 9 | component: () => , 10 | parameters: { 11 | // More on how to position stories at: https://storybook.js.org/docs/configure/story-layout 12 | layout: "fullscreen", 13 | }, 14 | } satisfies Meta; 15 | 16 | export default meta; 17 | 18 | export const TokenAndChainSelectorModalExample = () => { 19 | const modal = useModal(TokenAndChainSelectorModal); 20 | 21 | return ( 22 | 23 | 24 | 33 | 42 | 43 | 44 | ); 45 | }; 46 | -------------------------------------------------------------------------------- /packages/widget/src/stories/VirtualList.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from "@storybook/react"; 2 | import { VirtualList } from "@/components/VirtualList"; 3 | import { ThemeProvider } from "styled-components"; 4 | import { lightTheme } from "@/widget/theme"; 5 | 6 | const meta = { 7 | title: "Components/VirtualList", 8 | component: (props) => ( 9 | 10 | 11 | 12 | ), 13 | parameters: { 14 | // More on how to position stories at: https://storybook.js.org/docs/configure/story-layout 15 | layout: "fullscreen", 16 | }, 17 | } satisfies Meta; 18 | 19 | type Story = StoryObj; 20 | 21 | export default meta; 22 | 23 | export const ListExample: Story = { 24 | args: { 25 | listItems: Array.from({ length: 500 }) as number[], 26 | height: 500, 27 | itemHeight: 100, 28 | renderItem: (item, index) => ( 29 |
30 | {index}: {item as number} 31 |
32 | ), 33 | itemKey: (item) => item as string, 34 | }, 35 | }; 36 | -------------------------------------------------------------------------------- /packages/widget/src/stories/WalletSelectorModal.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta } from "@storybook/react"; 2 | import NiceModal, { useModal } from "@ebay/nice-modal-react"; 3 | import { Row } from "@/components/Layout"; 4 | import { defaultTheme, lightTheme } from "@/widget/theme"; 5 | 6 | import { 7 | WalletSelectorModal, 8 | WalletSelectorModalProps, 9 | } from "@/modals/WalletSelectorModal/WalletSelectorModal"; 10 | 11 | const meta = { 12 | title: "Modals/WalletSelectorModal", 13 | component: (props) => , 14 | parameters: { 15 | // More on how to position stories at: https://storybook.js.org/docs/configure/story-layout 16 | layout: "fullscreen", 17 | }, 18 | } satisfies Meta; 19 | 20 | export default meta; 21 | 22 | export const WalletSelectorModalExample = (props: WalletSelectorModalProps) => { 23 | const modal = useModal(WalletSelectorModal); 24 | 25 | return ( 26 | 27 | 28 | 38 | 48 | 49 | 50 | ); 51 | }; 52 | -------------------------------------------------------------------------------- /packages/widget/src/stories/Widget.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta } from "@storybook/react"; 2 | import { ShowSwapWidget, SwapWidget, SwapWidgetProps } from "@/widget/Widget"; 3 | import { defaultTheme, lightTheme, Theme } from "@/widget/theme"; 4 | import NiceModal from "@ebay/nice-modal-react"; 5 | import { styled } from "styled-components"; 6 | import { ReactElement } from "react"; 7 | 8 | type Props = SwapWidgetProps & { theme: Theme; button?: ReactElement }; 9 | 10 | export const Widget = (props: Props) => ; 11 | 12 | export const Modal = (props: Props) => { 13 | return ( 14 | 15 | 16 | 17 | ); 18 | }; 19 | 20 | const StyledCustomButton = styled.button` 21 | border: none; 22 | border-radius: 12px; 23 | padding: 20px; 24 | color: white; 25 | background-color: navy; 26 | `; 27 | 28 | const meta = { 29 | title: "Widget", 30 | component: Widget, 31 | args: { 32 | theme: defaultTheme, 33 | button: undefined, 34 | }, 35 | argTypes: { 36 | theme: { 37 | options: ["defaultTheme", "lightTheme"], 38 | mapping: { 39 | defaultTheme: defaultTheme, 40 | lightTheme: lightTheme, 41 | }, 42 | }, 43 | button: { 44 | options: ["default", "custom"], 45 | mapping: { 46 | default: undefined, 47 | custom: ( 48 | 49 | Custom button
50 | click to open widget 51 |
52 | ), 53 | }, 54 | }, 55 | }, 56 | } satisfies Meta; 57 | 58 | export default meta; 59 | -------------------------------------------------------------------------------- /packages/widget/src/stories/renderLightAndDarkTheme.tsx: -------------------------------------------------------------------------------- 1 | import { cloneElement, ComponentProps, CSSProperties, ReactNode } from "react"; 2 | import { styled, ThemeProvider } from "styled-components"; 3 | import { defaultTheme, lightTheme } from "@/widget/theme"; 4 | import { Column } from "@/components/Layout"; 5 | import React from "react"; 6 | 7 | export const renderLightAndDarkTheme = ( 8 | render: ReactNode, 9 | style?: CSSProperties, 10 | row?: boolean, 11 | ) => { 12 | return ( 13 | 14 | {render} 15 | {render} 16 | 17 | ); 18 | }; 19 | export const renderLightAndDarkThemeSeperateProps = ( 20 | render: React.ReactElement>, 21 | defaultProps: Partial>, 22 | lightProps: Partial>, 23 | ): React.ReactElement => { 24 | const renderDarkElement = cloneElement(render, defaultProps); 25 | const renderLightElement = cloneElement(render, lightProps); 26 | 27 | return ( 28 | 29 | {renderDarkElement} 30 | {renderLightElement} 31 | 32 | ); 33 | }; 34 | export const StyledWrapper = styled(Column)<{ row?: boolean }>` 35 | ${({ row }) => row && "flex-direction: row;"} 36 | padding: 20px; 37 | `; 38 | -------------------------------------------------------------------------------- /packages/widget/src/utils/bech32.ts: -------------------------------------------------------------------------------- 1 | import { bech32, bech32m } from "bech32"; 2 | 3 | // Create wrapped versions of bech32 and bech32m with default limits of 1023 characters 4 | const wrappedBech32 = { 5 | ...bech32, 6 | decode: (str: string, LIMIT = 1023) => bech32.decode(str, LIMIT), 7 | decodeUnsafe: (str: string, LIMIT = 1023) => bech32.decodeUnsafe(str, LIMIT), 8 | }; 9 | 10 | const wrappedBech32m = { 11 | ...bech32m, 12 | decode: (str: string, LIMIT = 1023) => bech32m.decode(str, LIMIT), 13 | decodeUnsafe: (str: string, LIMIT = 1023) => bech32m.decodeUnsafe(str, LIMIT), 14 | }; 15 | 16 | export { wrappedBech32 as bech32, wrappedBech32m as bech32m }; 17 | -------------------------------------------------------------------------------- /packages/widget/src/utils/colors.ts: -------------------------------------------------------------------------------- 1 | export const opacityToHex = (p: number) => { 2 | const percent = Math.max(0, Math.min(100, p)); // bound percent from 0 to 100 3 | const intValue = Math.round((percent / 100) * 255); // map percent to nearest integer (0 - 255) 4 | const hexValue = intValue.toString(16); // get hexadecimal representation 5 | return hexValue.padStart(2, "0").toUpperCase(); // format with leading 0 and upper case characters 6 | }; 7 | 8 | export const getHexColor = (color: string) => { 9 | const context = document.createElement("canvas").getContext("2d"); 10 | if (context) { 11 | context.fillStyle = color; 12 | return context.fillStyle; 13 | } 14 | return color; 15 | }; 16 | 17 | export const getBrandButtonTextColor = (color: string) => { 18 | color = getHexColor(color); 19 | 20 | const r = parseInt(color.slice(1, 3), 16); 21 | const g = parseInt(color.slice(3, 5), 16); 22 | const b = parseInt(color.slice(5, 7), 16); 23 | 24 | const hsp = Math.sqrt(0.299 * (r * r) + 0.587 * (g * g) + 0.114 * (b * b)); 25 | 26 | const typeOfColor = hsp > 127.5 ? "light" : "dark"; 27 | return typeOfColor === "light" ? "black" : "white"; 28 | }; 29 | -------------------------------------------------------------------------------- /packages/widget/src/utils/countdownTimer.ts: -------------------------------------------------------------------------------- 1 | export function createCountdownTimer({ 2 | duration, 3 | onUpdate, 4 | onComplete, 5 | }: { 6 | duration: number; 7 | onUpdate?: (remainingTime: number) => void; 8 | onComplete?: () => void; 9 | }) { 10 | let start: number | null = null; 11 | let animationFrameId: number | null = null; 12 | 13 | function step(timestamp: number) { 14 | if (!start) start = timestamp; 15 | 16 | const elapsed = timestamp - start; 17 | const remainingTime = Math.max(duration - elapsed, 0); 18 | 19 | onUpdate?.(remainingTime); 20 | 21 | if (remainingTime > 0) { 22 | animationFrameId = requestAnimationFrame(step); 23 | } else { 24 | onComplete?.(); 25 | } 26 | } 27 | 28 | function startCountdown() { 29 | if (animationFrameId !== null) { 30 | cancelAnimationFrame(animationFrameId); 31 | } 32 | start = null; 33 | animationFrameId = requestAnimationFrame(step); 34 | } 35 | 36 | function stopCountdown() { 37 | if (animationFrameId !== null) { 38 | cancelAnimationFrame(animationFrameId); 39 | animationFrameId = null; 40 | } 41 | } 42 | 43 | return { startCountdown, stopCountdown }; 44 | } 45 | -------------------------------------------------------------------------------- /packages/widget/src/utils/crypto.ts: -------------------------------------------------------------------------------- 1 | import { DEFAULT_DECIMAL_PLACES } from "@/constants/widget"; 2 | import { BigNumber } from "bignumber.js"; 3 | 4 | export const convertHumanReadableAmountToCryptoAmount = ( 5 | humanReadableAmount: number | string, 6 | decimals = DEFAULT_DECIMAL_PLACES, 7 | ): string => { 8 | const cryptoAmount = new BigNumber(humanReadableAmount).shiftedBy(decimals); 9 | return cryptoAmount.toFixed(0); 10 | }; 11 | 12 | export const convertTokenAmountToHumanReadableAmount = ( 13 | tokenAmount: number | string, 14 | decimals = DEFAULT_DECIMAL_PLACES, 15 | ): string => { 16 | const humanReadableAmount = new BigNumber(tokenAmount).shiftedBy(-decimals); 17 | return humanReadableAmount 18 | .toFixed(decimals, BigNumber.ROUND_DOWN) 19 | .replace(/(\.\d*?[1-9])(?:0+|\.0*)$/, "$1"); 20 | }; 21 | 22 | export const getTruncatedAddress = (address?: string, extraShort?: boolean): string => { 23 | if (!address) return ""; 24 | if (extraShort) return `${address.slice(0, 6)}…${address.slice(-3)}`; 25 | return `${address.slice(0, 9)}…${address.slice(-5)}`; 26 | }; 27 | 28 | export const hasAmountChanged = ( 29 | newAmount: string, 30 | oldAmount: string, 31 | decimals: number = DEFAULT_DECIMAL_PLACES, 32 | ) => { 33 | const newAmountBN = new BigNumber(newAmount).decimalPlaces(decimals, BigNumber.ROUND_DOWN); 34 | const oldAmountBN = new BigNumber(oldAmount).decimalPlaces(decimals, BigNumber.ROUND_DOWN); 35 | return !newAmountBN.eq(oldAmountBN); 36 | }; 37 | -------------------------------------------------------------------------------- /packages/widget/src/utils/date.test.ts: -------------------------------------------------------------------------------- 1 | import { test, expect } from "@playwright/test"; 2 | import { getMobileDateFormat } from "./date"; 3 | 4 | test.describe("getMobileDateFormat", () => { 5 | test("formats date in UTC with zero padding", () => { 6 | const date = new Date("2024-03-02T09:05:00Z"); 7 | const formatted = getMobileDateFormat(date, "UTC"); 8 | expect(formatted).toBe("09:05 UTC 03/02/24"); 9 | }); 10 | 11 | test("formats date in Asia/Jakarta timezone correctly", () => { 12 | const date = new Date("2024-03-02T09:05:00Z"); 13 | const formatted = getMobileDateFormat(date, "Asia/Jakarta"); 14 | expect(formatted).toBe("16:05 GMT+7 03/02/24"); 15 | }); 16 | 17 | test("formats date in local timezone if timezone is not provided", () => { 18 | const date = new Date("2024-03-02T09:05:00Z"); 19 | const formatted = getMobileDateFormat(date, ""); // Local time fallback 20 | expect(formatted).toMatch(/\d{2}:\d{2} .+ \d{2}\/\d{2}\/\d{2}/); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /packages/widget/src/utils/date.ts: -------------------------------------------------------------------------------- 1 | export const getMobileDateFormat = (date: Date, timeZone?: string) => { 2 | const options: Intl.DateTimeFormatOptions = { 3 | timeZone: timeZone || undefined, // undefined means local time 4 | hour: "2-digit", 5 | minute: "2-digit", 6 | timeZoneName: "short", 7 | day: "2-digit", 8 | month: "2-digit", 9 | year: "2-digit", 10 | hour12: false, 11 | }; 12 | 13 | const formatter = new Intl.DateTimeFormat("en-US", options); 14 | const parts = formatter.formatToParts(date); 15 | 16 | const lookup = (type: string) => parts.find((p) => p.type === type)?.value ?? ""; 17 | 18 | const hours = lookup("hour"); 19 | const minutes = lookup("minute"); 20 | const tz = lookup("timeZoneName"); 21 | const day = lookup("day"); 22 | const month = lookup("month"); 23 | const year = lookup("year"); 24 | 25 | return `${hours}:${minutes} ${tz} ${month}/${day}/${year}`; 26 | }; 27 | -------------------------------------------------------------------------------- /packages/widget/src/utils/error.ts: -------------------------------------------------------------------------------- 1 | export function isUserRejectedRequestError(input: unknown): input is Error { 2 | if (input instanceof Error) { 3 | if ( 4 | // keplr | metamask 5 | input.message.toLowerCase().includes("rejected") || 6 | // leap 7 | input.message.toLowerCase().includes("declined") || 8 | input.message.toLowerCase().includes("denied") || 9 | // @ts-expect-error common user rejected request error code 10 | input.code === 4001 11 | ) { 12 | return true; 13 | } 14 | } 15 | return false; 16 | } 17 | -------------------------------------------------------------------------------- /packages/widget/src/utils/intl.ts: -------------------------------------------------------------------------------- 1 | export function formatMaxFraction(amount: string | number, fraction = 6) { 2 | const amountNumber = typeof amount === "string" ? parseFloat(amount) : amount; 3 | const { format } = new Intl.NumberFormat("en-US", { 4 | maximumFractionDigits: fraction, 5 | }); 6 | return format(amountNumber); 7 | } 8 | 9 | export function formatPercent(amount: string | number) { 10 | const amountNumber = typeof amount === "string" ? parseFloat(amount) : amount; 11 | return percentFormatter.format(amountNumber); 12 | } 13 | 14 | export function formatShortDate(date: string | Date) { 15 | const dateObject = typeof date === "string" ? new Date(date) : date; 16 | return shortDateFormatter.format(dateObject); 17 | } 18 | 19 | export function formatUSD(amount: string | number) { 20 | const amountNumber = typeof amount === "string" ? parseFloat(amount) : amount; 21 | const minDisplayable = 1 / Math.pow(10, 2); 22 | 23 | if (amountNumber > 0 && amountNumber < minDisplayable) { 24 | return `< ${usdFormatter.format(minDisplayable)}`; 25 | } 26 | 27 | return usdFormatter.format(amountNumber); 28 | } 29 | 30 | const percentFormatter = new Intl.NumberFormat("en-US", { 31 | style: "percent", 32 | maximumFractionDigits: 2, 33 | }); 34 | 35 | const shortDateFormatter = new Intl.DateTimeFormat("en-US", { 36 | month: "short", 37 | day: "numeric", 38 | }); 39 | 40 | const usdFormatter = new Intl.NumberFormat("en-US", { 41 | style: "currency", 42 | currency: "USD", 43 | maximumFractionDigits: 2, 44 | }); 45 | -------------------------------------------------------------------------------- /packages/widget/src/utils/misc.tsx: -------------------------------------------------------------------------------- 1 | import { atomWithStorage } from "jotai/utils"; 2 | import { SyncStorage } from "jotai/vanilla/utils/atomWithStorage"; 3 | 4 | export const withBoundProps =

( 5 | WrappedComponent: React.ComponentType

, 6 | boundProps: Partial

, 7 | ) => { 8 | return (props: Partial

): React.ReactElement => { 9 | const combinedProps = { 10 | ...boundProps, 11 | ...props, 12 | } as P; 13 | return ; 14 | }; 15 | }; 16 | 17 | export const copyToClipboard = (string?: string) => { 18 | if (string) { 19 | navigator.clipboard.writeText(string); 20 | } 21 | }; 22 | 23 | export function atomWithStorageNoCrossTabSync(storageKey: string, initialValue: T) { 24 | const defaultStorage: SyncStorage = { 25 | getItem: (key) => { 26 | if (typeof window === "undefined") return; 27 | const storedValue = localStorage.getItem(key); 28 | if (!storedValue) return initialValue; 29 | 30 | try { 31 | return JSON.parse(storedValue); 32 | } catch (_error) { 33 | return initialValue; 34 | } 35 | }, 36 | setItem: (key, newValue) => { 37 | if (typeof window === "undefined") return; 38 | localStorage.setItem(key, JSON.stringify(newValue)); 39 | }, 40 | removeItem: (key) => { 41 | if (typeof window === "undefined") return; 42 | localStorage.removeItem(key); 43 | }, 44 | }; 45 | 46 | return atomWithStorage(storageKey, initialValue, defaultStorage, { getOnInit: true }); 47 | } 48 | -------------------------------------------------------------------------------- /packages/widget/src/utils/number.test.ts: -------------------------------------------------------------------------------- 1 | import { test, expect } from "@playwright/test"; 2 | import { 3 | limitDecimalsDisplayed, 4 | convertSecondsToMinutesOrHours, 5 | } from "./number"; 6 | 7 | 8 | test.describe("limitDecimalsDisplayed", () => { 9 | test("returns empty string for undefined", () => { 10 | expect(limitDecimalsDisplayed(undefined)).toBe(""); 11 | }); 12 | 13 | test("leaves string shorter than limit", () => { 14 | expect(limitDecimalsDisplayed("123.45", 6)).toBe("123.45"); 15 | }); 16 | 17 | test("truncates string with extra decimals", () => { 18 | expect(limitDecimalsDisplayed("1.1234567", 4)).toBe("1.1234"); 19 | }); 20 | 21 | test("truncates number input", () => { 22 | expect(limitDecimalsDisplayed(1.987654321, 2)).toBe("1.98"); 23 | }); 24 | }); 25 | 26 | test.describe("convertSecondsToMinutesOrHours", () => { 27 | test("formats seconds under a minute", () => { 28 | expect(convertSecondsToMinutesOrHours(30)).toBe("30 secs"); 29 | }); 30 | 31 | test("formats seconds under an hour", () => { 32 | expect(convertSecondsToMinutesOrHours(90)).toBe("2 mins"); 33 | }); 34 | 35 | test("formats seconds to hours", () => { 36 | expect(convertSecondsToMinutesOrHours(3600)).toBe("1 hr"); 37 | }); 38 | 39 | test("returns undefined for falsy input", () => { 40 | expect(convertSecondsToMinutesOrHours()).toBeUndefined(); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /packages/widget/src/utils/os.ts: -------------------------------------------------------------------------------- 1 | export const isBrowser = typeof window !== "undefined" && typeof window.navigator !== "undefined"; 2 | 3 | export function isAndroid() { 4 | if (!isBrowser) { 5 | return false; 6 | } 7 | return isMobile() && window.navigator.userAgent.toLowerCase().includes("android"); 8 | } 9 | 10 | export function isIos() { 11 | if (!isBrowser) { 12 | return false; 13 | } 14 | return isMobile() && window.navigator.userAgent.toLowerCase().match(/iphone|ipad/u); 15 | } 16 | 17 | export function isMobile() { 18 | if (!isBrowser) { 19 | return false; 20 | } 21 | return ( 22 | window.matchMedia("(pointer:coarse)").matches || 23 | !!window.navigator.userAgent.match(/Android|webOS|iPhone|iPad|iPod|BlackBerry|Opera Mini/u) 24 | ); 25 | } 26 | 27 | export function isWindows() { 28 | return ( 29 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 30 | (window.navigator as any).userAgentData?.platform.startsWith("Win") ?? 31 | navigator.platform.startsWith("Win") 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /packages/widget/src/utils/transitions.ts: -------------------------------------------------------------------------------- 1 | export const ANIMATION_TIMINGS = { 2 | fast: "70ms", 3 | medium: "250ms", 4 | slow: "500ms", 5 | extraSlow: "800ms", 6 | } as const; 7 | 8 | export const EASINGS = { 9 | easeInOut: "cubic-bezier(0.4, 0, 0.2, 1)", 10 | easeOut: "cubic-bezier(0.0, 0, 0.2, 1)", 11 | easeIn: "cubic-bezier(0.4, 0, 1, 1)", 12 | sharp: "cubic-bezier(0.4, 0, 0.6, 1)", 13 | } as const; 14 | 15 | export const transition = ( 16 | properties: string[] = ["all"], 17 | duration: keyof typeof ANIMATION_TIMINGS = "medium", 18 | easing: keyof typeof EASINGS = "easeInOut", 19 | ) => ` 20 | transition: ${properties.join(", ")} ${ANIMATION_TIMINGS[duration]} ${EASINGS[easing]}; 21 | `; 22 | -------------------------------------------------------------------------------- /packages/widget/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /packages/widget/src/widget/initAmplitude.ts: -------------------------------------------------------------------------------- 1 | import { init, add } from "@amplitude/analytics-browser"; 2 | import { version } from "../../package.json"; 3 | import { sessionReplayPlugin } from '@amplitude/plugin-session-replay-browser'; 4 | 5 | let isAmplitudeInitialized = false; 6 | 7 | export const initAmplitude = () => { 8 | if (isAmplitudeInitialized) return; 9 | 10 | init("14616a575f32087cf0403ab8f3ea3ce0", { 11 | appVersion: version, 12 | }); 13 | 14 | const plugin = sessionReplayPlugin({ 15 | sampleRate: 1, 16 | }); 17 | add(plugin); 18 | }; 19 | -------------------------------------------------------------------------------- /packages/widget/src/widget/initSentry.ts: -------------------------------------------------------------------------------- 1 | import { 2 | breadcrumbsIntegration, 3 | browserSessionIntegration, 4 | dedupeIntegration, 5 | functionToStringIntegration, 6 | httpContextIntegration, 7 | inboundFiltersIntegration, 8 | init, 9 | linkedErrorsIntegration, 10 | replayIntegration, 11 | } from "@sentry/react"; 12 | 13 | let isSentryInitialized = false; 14 | 15 | export const initSentry = () => { 16 | if (isSentryInitialized) return; 17 | init({ 18 | dsn: "https://10ce608bdd1c68a13d3849d6b242333c@o4504768725909504.ingest.us.sentry.io/4508485201231872", 19 | tunnel: "https://go.skip.build/api/sentry", 20 | defaultIntegrations: false, 21 | integrations: [ 22 | breadcrumbsIntegration(), 23 | dedupeIntegration(), 24 | functionToStringIntegration(), 25 | httpContextIntegration(), 26 | inboundFiltersIntegration(), 27 | linkedErrorsIntegration(), 28 | browserSessionIntegration(), 29 | replayIntegration({ 30 | maskAllText: false, 31 | maskAllInputs: false, 32 | blockAllMedia: false, 33 | networkDetailAllowUrls: [/^https:\/\/go\.skip\.build\//], 34 | networkRequestHeaders: ["X-Custom-Header"], 35 | networkResponseHeaders: ["X-Custom-Header"], 36 | useCompression: false, 37 | }), 38 | ], 39 | }); 40 | isSentryInitialized = true; 41 | }; 42 | -------------------------------------------------------------------------------- /packages/widget/src/widget/useInitDefaultRoute.ts: -------------------------------------------------------------------------------- 1 | import { defaultRouteAtom, setRouteToDefaultRouteAtom } from "@/state/route"; 2 | import { skipAssetsAtom } from "@/state/skipClient"; 3 | import { useSetAtom, useAtom } from "jotai"; 4 | import { useEffect } from "react"; 5 | 6 | export type DefaultRouteConfig = { 7 | amountIn?: number; 8 | amountOut?: number; 9 | srcChainId?: string; 10 | srcAssetDenom?: string; 11 | destChainId?: string; 12 | destAssetDenom?: string; 13 | srcLocked?: boolean; 14 | destLocked?: boolean; 15 | }; 16 | 17 | export const useInitDefaultRoute = (defaultRoute?: DefaultRouteConfig) => { 18 | const setDefaultRoute = useSetAtom(defaultRouteAtom); 19 | const setRouteToDefaultRoute = useSetAtom(setRouteToDefaultRouteAtom); 20 | const [{ data: assets }] = useAtom(skipAssetsAtom); 21 | 22 | useEffect(() => { 23 | if (!defaultRoute) return; 24 | setDefaultRoute(defaultRoute); 25 | setRouteToDefaultRoute(assets); 26 | }, [assets, defaultRoute, setDefaultRoute, setRouteToDefaultRoute]); 27 | }; 28 | -------------------------------------------------------------------------------- /packages/widget/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", 5 | "target": "ES2020", 6 | "useDefineForClassFields": true, 7 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 8 | "module": "ESNext", 9 | "skipLibCheck": true, 10 | "noEmitOnError": false, // Allow emitting files even if there are type errors 11 | 12 | /* Bundler mode */ 13 | "moduleResolution": "bundler", 14 | "allowImportingTsExtensions": true, 15 | "importsNotUsedAsValues": "remove", 16 | "resolveJsonModule": true, 17 | "isolatedModules": true, 18 | "moduleDetection": "force", 19 | "noEmit": true, 20 | "jsx": "preserve", 21 | 22 | /* Linting */ 23 | "strict": true, 24 | "noUnusedLocals": true, 25 | "noUnusedParameters": true, 26 | "noFallthroughCasesInSwitch": true, 27 | 28 | /* Aliases */ 29 | "baseUrl": ".", 30 | "paths": { 31 | "@/*": ["src/*"] 32 | } 33 | }, 34 | "exclude": ["build", "src/stories", "scripts"], 35 | "include": ["src", "web-component", "src/**/*.json", "package.json"] 36 | } 37 | -------------------------------------------------------------------------------- /packages/widget/tsconfig.webpack.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "jsx": "react-jsx" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/widget/web-component/README.md: -------------------------------------------------------------------------------- 1 | # Skip Go Widget 2 | 3 | We document everything about Skip Go Widget in [docs.skip.build/go/widget](https://docs.skip.build/go/widget) 4 | -------------------------------------------------------------------------------- /packages/widget/web-component/index.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/consistent-type-definitions */ 2 | declare const WEB_COMPONENT_NAME = "skip-widget"; 3 | 4 | declare global { 5 | interface HTMLElementTagNameMap { 6 | [WEB_COMPONENT_NAME]: HTMLElement; 7 | } 8 | } 9 | 10 | export {}; 11 | -------------------------------------------------------------------------------- /packages/widget/web-component/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@skip-go/widget-web-component", 3 | "version": "3.10.6", 4 | "description": "Swap widget web component", 5 | "exports": { 6 | ".": { 7 | "import": "./build/index.js", 8 | "types": "./build/index.d.ts" 9 | } 10 | }, 11 | "types": "./build/index.d.ts", 12 | "files": [ 13 | "build", 14 | "README.md" 15 | ] 16 | } -------------------------------------------------------------------------------- /packages/widget/web-component/syncVersion.cjs: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | /* eslint-disable @typescript-eslint/no-require-imports */ 3 | const path = require("path"); 4 | const fs = require("fs/promises"); 5 | 6 | module.exports = async function syncVersion() { 7 | const basePackageJson = require("../package.json"); 8 | const webComponentPackageJson = require("./package.json"); 9 | 10 | webComponentPackageJson.version = basePackageJson.version; 11 | 12 | const targetFile = path.resolve(path.resolve(__dirname, "../web-component"), "package.json"); 13 | 14 | await fs.writeFile(targetFile, JSON.stringify(webComponentPackageJson, null, 2), { 15 | encoding: "utf-8", 16 | }); 17 | }; 18 | -------------------------------------------------------------------------------- /vendor/index.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | 3 | const path = require('path'); 4 | 5 | const protoDirs = [ 6 | path.resolve(__dirname, '../node_modules/@protobufs/'), 7 | path.resolve(__dirname, 'cosmos-proto/proto'), 8 | path.resolve(__dirname, 'cosmos-sdk/proto'), 9 | path.resolve(__dirname, 'evmos/proto'), 10 | path.resolve(__dirname, 'noble-cctp/proto'), 11 | path.resolve(__dirname, '../local_vendor/initia/proto'), 12 | path.resolve(__dirname, '../local_vendor/OPinit/proto'), 13 | ]; 14 | 15 | module.exports = protoDirs; 16 | --------------------------------------------------------------------------------