@multiplatform.one/web3 7.23.1 → 7.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +150 -11
- package/dist/cjs/clients/evm.cjs +299 -11
- package/dist/cjs/clients/evm.native.js +500 -12
- package/dist/cjs/clients/evm.native.js.map +1 -1
- package/dist/cjs/clients/index.cjs +5 -0
- package/dist/cjs/clients/index.native.js +5 -0
- package/dist/cjs/clients/index.native.js.map +1 -1
- package/dist/cjs/clients/solana.cjs +39 -21
- package/dist/cjs/clients/solana.native.js +48 -23
- package/dist/cjs/clients/solana.native.js.map +1 -1
- package/dist/cjs/connectkit/GpgProvider.cjs +44 -0
- package/dist/cjs/connectkit/GpgProvider.native.js +51 -0
- package/dist/cjs/connectkit/GpgProvider.native.js.map +1 -0
- package/dist/cjs/connectkit/adapters/BitcoinAdapter.cjs +5 -14
- package/dist/cjs/connectkit/adapters/BitcoinAdapter.native.js +5 -15
- package/dist/cjs/connectkit/adapters/BitcoinAdapter.native.js.map +1 -1
- package/dist/cjs/connectkit/adapters/EthereumAdapter.cjs +6 -15
- package/dist/cjs/connectkit/adapters/EthereumAdapter.native.js +7 -16
- package/dist/cjs/connectkit/adapters/EthereumAdapter.native.js.map +1 -1
- package/dist/cjs/connectkit/adapters/SolanaAdapter.cjs +5 -14
- package/dist/cjs/connectkit/adapters/SolanaAdapter.native.js +5 -15
- package/dist/cjs/connectkit/adapters/SolanaAdapter.native.js.map +1 -1
- package/dist/cjs/connectkit/adapters/SuiAdapter.cjs +5 -14
- package/dist/cjs/connectkit/adapters/SuiAdapter.native.js +5 -15
- package/dist/cjs/connectkit/adapters/SuiAdapter.native.js.map +1 -1
- package/dist/cjs/connectkit/connectors/createWalletMessage.cjs +2 -2
- package/dist/cjs/connectkit/connectors/createWalletMessage.native.js +2 -2
- package/dist/cjs/connectkit/connectors/createWalletMessage.native.js.map +1 -1
- package/dist/cjs/connectkit/index.cjs +4 -3
- package/dist/cjs/connectkit/index.native.js +4 -3
- package/dist/cjs/connectkit/index.native.js.map +1 -1
- package/dist/cjs/connectkit/siwe/SIWEContext.native.js.map +1 -1
- package/dist/cjs/connectkit/siwe/SIWEProvider.cjs +18 -3
- package/dist/cjs/connectkit/siwe/SIWEProvider.native.js +18 -3
- package/dist/cjs/connectkit/siwe/SIWEProvider.native.js.map +1 -1
- package/dist/cjs/index.cjs +1 -0
- package/dist/cjs/index.native.js +1 -0
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/cjs/keycloak/KeycloakWalletLogin.cjs +255 -0
- package/dist/cjs/keycloak/KeycloakWalletLogin.native.js +33 -0
- package/dist/cjs/keycloak/KeycloakWalletLogin.native.js.map +1 -0
- package/dist/cjs/keycloak/browserFlow.cjs +83 -0
- package/dist/cjs/keycloak/browserFlow.native.js +90 -0
- package/dist/cjs/keycloak/browserFlow.native.js.map +1 -0
- package/dist/cjs/keycloak/device.cjs +36 -0
- package/dist/cjs/keycloak/device.native.js +45 -0
- package/dist/cjs/keycloak/device.native.js.map +1 -0
- package/dist/cjs/keycloak/directGrant.cjs +189 -0
- package/dist/cjs/keycloak/directGrant.native.js +233 -0
- package/dist/cjs/keycloak/directGrant.native.js.map +1 -0
- package/dist/cjs/keycloak/iife.cjs +4 -0
- package/dist/cjs/keycloak/iife.native.js +7 -0
- package/dist/cjs/keycloak/iife.native.js.map +1 -0
- package/dist/cjs/keycloak/index.cjs +53 -0
- package/dist/cjs/keycloak/index.native.js +56 -0
- package/dist/cjs/keycloak/index.native.js.map +1 -0
- package/dist/cjs/keycloak/keycloakWallet.cjs +214 -0
- package/dist/cjs/keycloak/keycloakWallet.native.js +285 -0
- package/dist/cjs/keycloak/keycloakWallet.native.js.map +1 -0
- package/dist/cjs/keycloak/mount.cjs +53 -0
- package/dist/cjs/keycloak/mount.native.js +33 -0
- package/dist/cjs/keycloak/mount.native.js.map +1 -0
- package/dist/cjs/keycloak/registerWallet.cjs +74 -0
- package/dist/cjs/keycloak/registerWallet.native.js +81 -0
- package/dist/cjs/keycloak/registerWallet.native.js.map +1 -0
- package/dist/cjs/keycloak/types.cjs +46 -0
- package/dist/cjs/keycloak/types.native.js +179 -0
- package/dist/cjs/keycloak/types.native.js.map +1 -0
- package/dist/cjs/keycloak/useDirectLogin.cjs +57 -0
- package/dist/cjs/keycloak/useDirectLogin.native.js +77 -0
- package/dist/cjs/keycloak/useDirectLogin.native.js.map +1 -0
- package/dist/cjs/keycloak/useWalletLogin.cjs +97 -0
- package/dist/cjs/keycloak/useWalletLogin.native.js +33 -0
- package/dist/cjs/keycloak/useWalletLogin.native.js.map +1 -0
- package/dist/cjs/message/buildWalletMessage.cjs +113 -0
- package/dist/cjs/message/buildWalletMessage.native.js +121 -0
- package/dist/cjs/message/buildWalletMessage.native.js.map +1 -0
- package/dist/cjs/message/index.cjs +29 -0
- package/dist/cjs/message/index.native.js +32 -0
- package/dist/cjs/message/index.native.js.map +1 -0
- package/dist/cjs/providers/WalletProvider.cjs +8 -2
- package/dist/cjs/siws/message.cjs +5 -16
- package/dist/cjs/siws/message.native.js +6 -16
- package/dist/cjs/siws/message.native.js.map +1 -1
- package/dist/cjs/solana.native.js.map +1 -1
- package/dist/esm/clients/evm.mjs +295 -12
- package/dist/esm/clients/evm.mjs.map +1 -1
- package/dist/esm/clients/evm.native.js +496 -13
- package/dist/esm/clients/evm.native.js.map +1 -1
- package/dist/esm/clients/index.mjs +2 -2
- package/dist/esm/clients/index.mjs.map +1 -1
- package/dist/esm/clients/index.native.js +2 -2
- package/dist/esm/clients/index.native.js.map +1 -1
- package/dist/esm/clients/solana.mjs +39 -21
- package/dist/esm/clients/solana.mjs.map +1 -1
- package/dist/esm/clients/solana.native.js +48 -23
- package/dist/esm/clients/solana.native.js.map +1 -1
- package/dist/esm/connectkit/GpgProvider.mjs +19 -0
- package/dist/esm/connectkit/GpgProvider.mjs.map +1 -0
- package/dist/esm/connectkit/GpgProvider.native.js +23 -0
- package/dist/esm/connectkit/GpgProvider.native.js.map +1 -0
- package/dist/esm/connectkit/adapters/BitcoinAdapter.mjs +5 -14
- package/dist/esm/connectkit/adapters/BitcoinAdapter.mjs.map +1 -1
- package/dist/esm/connectkit/adapters/BitcoinAdapter.native.js +5 -15
- package/dist/esm/connectkit/adapters/BitcoinAdapter.native.js.map +1 -1
- package/dist/esm/connectkit/adapters/EthereumAdapter.mjs +6 -15
- package/dist/esm/connectkit/adapters/EthereumAdapter.mjs.map +1 -1
- package/dist/esm/connectkit/adapters/EthereumAdapter.native.js +7 -16
- package/dist/esm/connectkit/adapters/EthereumAdapter.native.js.map +1 -1
- package/dist/esm/connectkit/adapters/SolanaAdapter.mjs +5 -14
- package/dist/esm/connectkit/adapters/SolanaAdapter.mjs.map +1 -1
- package/dist/esm/connectkit/adapters/SolanaAdapter.native.js +5 -15
- package/dist/esm/connectkit/adapters/SolanaAdapter.native.js.map +1 -1
- package/dist/esm/connectkit/adapters/SuiAdapter.mjs +5 -14
- package/dist/esm/connectkit/adapters/SuiAdapter.mjs.map +1 -1
- package/dist/esm/connectkit/adapters/SuiAdapter.native.js +5 -15
- package/dist/esm/connectkit/adapters/SuiAdapter.native.js.map +1 -1
- package/dist/esm/connectkit/connectors/createWalletMessage.mjs +1 -1
- package/dist/esm/connectkit/connectors/createWalletMessage.native.js +1 -1
- package/dist/esm/connectkit/index.mjs +3 -2
- package/dist/esm/connectkit/index.mjs.map +1 -1
- package/dist/esm/connectkit/index.native.js +3 -2
- package/dist/esm/connectkit/index.native.js.map +1 -1
- package/dist/esm/connectkit/siwe/SIWEContext.mjs.map +1 -1
- package/dist/esm/connectkit/siwe/SIWEContext.native.js.map +1 -1
- package/dist/esm/connectkit/siwe/SIWEProvider.mjs +19 -4
- package/dist/esm/connectkit/siwe/SIWEProvider.mjs.map +1 -1
- package/dist/esm/connectkit/siwe/SIWEProvider.native.js +19 -4
- package/dist/esm/connectkit/siwe/SIWEProvider.native.js.map +1 -1
- package/dist/esm/index.mjs +1 -0
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/index.native.js +1 -0
- package/dist/esm/index.native.js.map +1 -1
- package/dist/esm/keycloak/KeycloakWalletLogin.mjs +218 -0
- package/dist/esm/keycloak/KeycloakWalletLogin.mjs.map +1 -0
- package/dist/esm/keycloak/KeycloakWalletLogin.native.js +5 -0
- package/dist/esm/keycloak/KeycloakWalletLogin.native.js.map +1 -0
- package/dist/esm/keycloak/browserFlow.mjs +58 -0
- package/dist/esm/keycloak/browserFlow.mjs.map +1 -0
- package/dist/esm/keycloak/browserFlow.native.js +62 -0
- package/dist/esm/keycloak/browserFlow.native.js.map +1 -0
- package/dist/esm/keycloak/device.mjs +10 -0
- package/dist/esm/keycloak/device.mjs.map +1 -0
- package/dist/esm/keycloak/device.native.js +16 -0
- package/dist/esm/keycloak/device.native.js.map +1 -0
- package/dist/esm/keycloak/directGrant.mjs +164 -0
- package/dist/esm/keycloak/directGrant.mjs.map +1 -0
- package/dist/esm/keycloak/directGrant.native.js +205 -0
- package/dist/esm/keycloak/directGrant.native.js.map +1 -0
- package/dist/esm/keycloak/iife.mjs +5 -0
- package/dist/esm/keycloak/iife.mjs.map +1 -0
- package/dist/esm/keycloak/iife.native.js +5 -0
- package/dist/esm/keycloak/iife.native.js.map +1 -0
- package/dist/esm/keycloak/index.mjs +13 -0
- package/dist/esm/keycloak/index.mjs.map +1 -0
- package/dist/esm/keycloak/index.native.js +13 -0
- package/dist/esm/keycloak/index.native.js.map +1 -0
- package/dist/esm/keycloak/keycloakWallet.mjs +186 -0
- package/dist/esm/keycloak/keycloakWallet.mjs.map +1 -0
- package/dist/esm/keycloak/keycloakWallet.native.js +254 -0
- package/dist/esm/keycloak/keycloakWallet.native.js.map +1 -0
- package/dist/esm/keycloak/mount.mjs +28 -0
- package/dist/esm/keycloak/mount.mjs.map +1 -0
- package/dist/esm/keycloak/mount.native.js +5 -0
- package/dist/esm/keycloak/mount.native.js.map +1 -0
- package/dist/esm/keycloak/registerWallet.mjs +49 -0
- package/dist/esm/keycloak/registerWallet.mjs.map +1 -0
- package/dist/esm/keycloak/registerWallet.native.js +53 -0
- package/dist/esm/keycloak/registerWallet.native.js.map +1 -0
- package/dist/esm/keycloak/types.mjs +20 -0
- package/dist/esm/keycloak/types.mjs.map +1 -0
- package/dist/esm/keycloak/types.native.js +150 -0
- package/dist/esm/keycloak/types.native.js.map +1 -0
- package/dist/esm/keycloak/useDirectLogin.mjs +32 -0
- package/dist/esm/keycloak/useDirectLogin.mjs.map +1 -0
- package/dist/esm/keycloak/useDirectLogin.native.js +49 -0
- package/dist/esm/keycloak/useDirectLogin.native.js.map +1 -0
- package/dist/esm/keycloak/useWalletLogin.mjs +72 -0
- package/dist/esm/keycloak/useWalletLogin.mjs.map +1 -0
- package/dist/esm/keycloak/useWalletLogin.native.js +5 -0
- package/dist/esm/keycloak/useWalletLogin.native.js.map +1 -0
- package/dist/esm/message/buildWalletMessage.mjs +87 -0
- package/dist/esm/message/buildWalletMessage.mjs.map +1 -0
- package/dist/esm/message/buildWalletMessage.native.js +92 -0
- package/dist/esm/message/buildWalletMessage.native.js.map +1 -0
- package/dist/esm/message/index.mjs +3 -0
- package/dist/esm/message/index.mjs.map +1 -0
- package/dist/esm/message/index.native.js +3 -0
- package/dist/esm/message/index.native.js.map +1 -0
- package/dist/esm/providers/WalletProvider.mjs +9 -3
- package/dist/esm/providers/WalletProvider.mjs.map +1 -1
- package/dist/esm/siws/message.mjs +5 -16
- package/dist/esm/siws/message.mjs.map +1 -1
- package/dist/esm/siws/message.native.js +6 -16
- package/dist/esm/siws/message.native.js.map +1 -1
- package/dist/esm/solana.mjs.map +1 -1
- package/dist/esm/solana.native.js.map +1 -1
- package/dist/jsx/clients/evm.mjs +295 -12
- package/dist/jsx/clients/evm.mjs.map +1 -1
- package/dist/jsx/clients/evm.native.js +500 -12
- package/dist/jsx/clients/evm.native.js.map +1 -1
- package/dist/jsx/clients/index.mjs +2 -2
- package/dist/jsx/clients/index.mjs.map +1 -1
- package/dist/jsx/clients/index.native.js +5 -0
- package/dist/jsx/clients/index.native.js.map +1 -1
- package/dist/jsx/clients/solana.mjs +39 -21
- package/dist/jsx/clients/solana.mjs.map +1 -1
- package/dist/jsx/clients/solana.native.js +48 -23
- package/dist/jsx/clients/solana.native.js.map +1 -1
- package/dist/jsx/connectkit/GpgProvider.mjs +19 -0
- package/dist/jsx/connectkit/GpgProvider.mjs.map +1 -0
- package/dist/jsx/connectkit/GpgProvider.native.js +51 -0
- package/dist/jsx/connectkit/GpgProvider.native.js.map +1 -0
- package/dist/jsx/connectkit/adapters/BitcoinAdapter.mjs +5 -14
- package/dist/jsx/connectkit/adapters/BitcoinAdapter.mjs.map +1 -1
- package/dist/jsx/connectkit/adapters/BitcoinAdapter.native.js +5 -15
- package/dist/jsx/connectkit/adapters/BitcoinAdapter.native.js.map +1 -1
- package/dist/jsx/connectkit/adapters/EthereumAdapter.mjs +6 -15
- package/dist/jsx/connectkit/adapters/EthereumAdapter.mjs.map +1 -1
- package/dist/jsx/connectkit/adapters/EthereumAdapter.native.js +7 -16
- package/dist/jsx/connectkit/adapters/EthereumAdapter.native.js.map +1 -1
- package/dist/jsx/connectkit/adapters/SolanaAdapter.mjs +5 -14
- package/dist/jsx/connectkit/adapters/SolanaAdapter.mjs.map +1 -1
- package/dist/jsx/connectkit/adapters/SolanaAdapter.native.js +5 -15
- package/dist/jsx/connectkit/adapters/SolanaAdapter.native.js.map +1 -1
- package/dist/jsx/connectkit/adapters/SuiAdapter.mjs +5 -14
- package/dist/jsx/connectkit/adapters/SuiAdapter.mjs.map +1 -1
- package/dist/jsx/connectkit/adapters/SuiAdapter.native.js +5 -15
- package/dist/jsx/connectkit/adapters/SuiAdapter.native.js.map +1 -1
- package/dist/jsx/connectkit/connectors/createWalletMessage.mjs +1 -1
- package/dist/jsx/connectkit/connectors/createWalletMessage.native.js +2 -2
- package/dist/jsx/connectkit/index.mjs +3 -2
- package/dist/jsx/connectkit/index.mjs.map +1 -1
- package/dist/jsx/connectkit/index.native.js +4 -3
- package/dist/jsx/connectkit/index.native.js.map +1 -1
- package/dist/jsx/connectkit/siwe/SIWEContext.mjs.map +1 -1
- package/dist/jsx/connectkit/siwe/SIWEContext.native.js.map +1 -1
- package/dist/jsx/connectkit/siwe/SIWEProvider.mjs +19 -4
- package/dist/jsx/connectkit/siwe/SIWEProvider.mjs.map +1 -1
- package/dist/jsx/connectkit/siwe/SIWEProvider.native.js +18 -3
- package/dist/jsx/connectkit/siwe/SIWEProvider.native.js.map +1 -1
- package/dist/jsx/index.js +1 -0
- package/dist/jsx/index.js.map +1 -1
- package/dist/jsx/index.mjs +1 -0
- package/dist/jsx/index.mjs.map +1 -1
- package/dist/jsx/index.native.js +1 -0
- package/dist/jsx/index.native.js.map +1 -1
- package/dist/jsx/keycloak/KeycloakWalletLogin.mjs +218 -0
- package/dist/jsx/keycloak/KeycloakWalletLogin.mjs.map +1 -0
- package/dist/jsx/keycloak/KeycloakWalletLogin.native.js +33 -0
- package/dist/jsx/keycloak/KeycloakWalletLogin.native.js.map +1 -0
- package/dist/jsx/keycloak/browserFlow.mjs +58 -0
- package/dist/jsx/keycloak/browserFlow.mjs.map +1 -0
- package/dist/jsx/keycloak/browserFlow.native.js +90 -0
- package/dist/jsx/keycloak/browserFlow.native.js.map +1 -0
- package/dist/jsx/keycloak/device.mjs +10 -0
- package/dist/jsx/keycloak/device.mjs.map +1 -0
- package/dist/jsx/keycloak/device.native.js +45 -0
- package/dist/jsx/keycloak/device.native.js.map +1 -0
- package/dist/jsx/keycloak/directGrant.mjs +164 -0
- package/dist/jsx/keycloak/directGrant.mjs.map +1 -0
- package/dist/jsx/keycloak/directGrant.native.js +233 -0
- package/dist/jsx/keycloak/directGrant.native.js.map +1 -0
- package/dist/jsx/keycloak/iife.mjs +5 -0
- package/dist/jsx/keycloak/iife.mjs.map +1 -0
- package/dist/jsx/keycloak/iife.native.js +7 -0
- package/dist/jsx/keycloak/iife.native.js.map +1 -0
- package/dist/jsx/keycloak/index.mjs +13 -0
- package/dist/jsx/keycloak/index.mjs.map +1 -0
- package/dist/jsx/keycloak/index.native.js +56 -0
- package/dist/jsx/keycloak/index.native.js.map +1 -0
- package/dist/jsx/keycloak/keycloakWallet.mjs +186 -0
- package/dist/jsx/keycloak/keycloakWallet.mjs.map +1 -0
- package/dist/jsx/keycloak/keycloakWallet.native.js +285 -0
- package/dist/jsx/keycloak/keycloakWallet.native.js.map +1 -0
- package/dist/jsx/keycloak/mount.mjs +28 -0
- package/dist/jsx/keycloak/mount.mjs.map +1 -0
- package/dist/jsx/keycloak/mount.native.js +33 -0
- package/dist/jsx/keycloak/mount.native.js.map +1 -0
- package/dist/jsx/keycloak/registerWallet.mjs +49 -0
- package/dist/jsx/keycloak/registerWallet.mjs.map +1 -0
- package/dist/jsx/keycloak/registerWallet.native.js +81 -0
- package/dist/jsx/keycloak/registerWallet.native.js.map +1 -0
- package/dist/jsx/keycloak/types.mjs +20 -0
- package/dist/jsx/keycloak/types.mjs.map +1 -0
- package/dist/jsx/keycloak/types.native.js +179 -0
- package/dist/jsx/keycloak/types.native.js.map +1 -0
- package/dist/jsx/keycloak/useDirectLogin.mjs +32 -0
- package/dist/jsx/keycloak/useDirectLogin.mjs.map +1 -0
- package/dist/jsx/keycloak/useDirectLogin.native.js +77 -0
- package/dist/jsx/keycloak/useDirectLogin.native.js.map +1 -0
- package/dist/jsx/keycloak/useWalletLogin.mjs +72 -0
- package/dist/jsx/keycloak/useWalletLogin.mjs.map +1 -0
- package/dist/jsx/keycloak/useWalletLogin.native.js +33 -0
- package/dist/jsx/keycloak/useWalletLogin.native.js.map +1 -0
- package/dist/jsx/message/buildWalletMessage.mjs +87 -0
- package/dist/jsx/message/buildWalletMessage.mjs.map +1 -0
- package/dist/jsx/message/buildWalletMessage.native.js +121 -0
- package/dist/jsx/message/buildWalletMessage.native.js.map +1 -0
- package/dist/jsx/message/index.mjs +3 -0
- package/dist/jsx/message/index.mjs.map +1 -0
- package/dist/jsx/message/index.native.js +32 -0
- package/dist/jsx/message/index.native.js.map +1 -0
- package/dist/jsx/providers/WalletProvider.mjs +9 -3
- package/dist/jsx/providers/WalletProvider.mjs.map +1 -1
- package/dist/jsx/siws/message.mjs +5 -16
- package/dist/jsx/siws/message.mjs.map +1 -1
- package/dist/jsx/siws/message.native.js +6 -16
- package/dist/jsx/siws/message.native.js.map +1 -1
- package/dist/jsx/solana.mjs.map +1 -1
- package/dist/jsx/solana.native.js.map +1 -1
- package/dist/keycloak/login.iife.js +3604 -0
- package/package.json +27 -8
- package/src/clients/evm.spec.ts +644 -10
- package/src/clients/evm.ts +498 -10
- package/src/clients/index.ts +8 -0
- package/src/clients/solana.spec.ts +116 -0
- package/src/clients/solana.ts +51 -31
- package/src/connectkit/GpgProvider.tsx +16 -0
- package/src/connectkit/adapters/BitcoinAdapter.ts +5 -16
- package/src/connectkit/adapters/EthereumAdapter.ts +6 -17
- package/src/connectkit/adapters/SolanaAdapter.ts +5 -16
- package/src/connectkit/adapters/SuiAdapter.ts +5 -16
- package/src/connectkit/connectors/createWalletMessage.ts +1 -1
- package/src/connectkit/index.ts +2 -6
- package/src/connectkit/openConnector/modal.spec.tsx +1 -1
- package/src/connectkit/siwe/SIWEContext.tsx +6 -2
- package/src/connectkit/siwe/SIWEProvider.tsx +22 -5
- package/src/index.ts +3 -0
- package/src/keycloak/KeycloakWalletLogin.native.tsx +7 -0
- package/src/keycloak/KeycloakWalletLogin.spec.tsx +168 -0
- package/src/keycloak/KeycloakWalletLogin.tsx +251 -0
- package/src/keycloak/browserFlow.spec.ts +108 -0
- package/src/keycloak/browserFlow.ts +84 -0
- package/src/keycloak/device.ts +17 -0
- package/src/keycloak/directGrant.spec.ts +331 -0
- package/src/keycloak/directGrant.ts +214 -0
- package/src/keycloak/iife.ts +3 -0
- package/src/keycloak/index.ts +44 -0
- package/src/keycloak/keycloakWallet.spec.ts +216 -0
- package/src/keycloak/keycloakWallet.ts +275 -0
- package/src/keycloak/mount.native.ts +11 -0
- package/src/keycloak/mount.tsx +45 -0
- package/src/keycloak/nonceReset.spec.tsx +205 -0
- package/src/keycloak/registerWallet.spec.ts +88 -0
- package/src/keycloak/registerWallet.ts +59 -0
- package/src/keycloak/types.ts +66 -0
- package/src/keycloak/useDirectLogin.spec.tsx +112 -0
- package/src/keycloak/useDirectLogin.ts +74 -0
- package/src/keycloak/useWalletLogin.native.ts +7 -0
- package/src/keycloak/useWalletLogin.ts +99 -0
- package/src/message/buildWalletMessage.spec.ts +291 -0
- package/src/message/buildWalletMessage.ts +150 -0
- package/src/message/index.ts +8 -0
- package/src/providers/WalletProvider.tsx +15 -2
- package/src/siws/message.ts +3 -22
- package/src/solana.ts +1 -0
- package/types/clients/evm.d.ts +144 -2
- package/types/clients/evm.d.ts.map +1 -1
- package/types/clients/index.d.ts +1 -1
- package/types/clients/index.d.ts.map +1 -1
- package/types/clients/solana.d.ts +14 -5
- package/types/clients/solana.d.ts.map +1 -1
- package/types/connectkit/GpgProvider.d.ts +10 -0
- package/types/connectkit/GpgProvider.d.ts.map +1 -0
- package/types/connectkit/adapters/BitcoinAdapter.d.ts.map +1 -1
- package/types/connectkit/adapters/EthereumAdapter.d.ts.map +1 -1
- package/types/connectkit/adapters/SolanaAdapter.d.ts.map +1 -1
- package/types/connectkit/adapters/SuiAdapter.d.ts.map +1 -1
- package/types/connectkit/connectors/createWalletMessage.d.ts +1 -1
- package/types/connectkit/connectors/createWalletMessage.d.ts.map +1 -1
- package/types/connectkit/index.d.ts +3 -2
- package/types/connectkit/index.d.ts.map +1 -1
- package/types/connectkit/siwe/SIWEContext.d.ts +2 -1
- package/types/connectkit/siwe/SIWEContext.d.ts.map +1 -1
- package/types/connectkit/siwe/SIWEProvider.d.ts.map +1 -1
- package/types/index.d.ts +1 -0
- package/types/index.d.ts.map +1 -1
- package/types/keycloak/KeycloakWalletLogin.d.ts +45 -0
- package/types/keycloak/KeycloakWalletLogin.d.ts.map +1 -0
- package/types/keycloak/KeycloakWalletLogin.native.d.ts +4 -0
- package/types/keycloak/KeycloakWalletLogin.native.d.ts.map +1 -0
- package/types/keycloak/browserFlow.d.ts +27 -0
- package/types/keycloak/browserFlow.d.ts.map +1 -0
- package/types/keycloak/device.d.ts +14 -0
- package/types/keycloak/device.d.ts.map +1 -0
- package/types/keycloak/directGrant.d.ts +43 -0
- package/types/keycloak/directGrant.d.ts.map +1 -0
- package/types/keycloak/iife.d.ts +2 -0
- package/types/keycloak/iife.d.ts.map +1 -0
- package/types/keycloak/index.d.ts +12 -0
- package/types/keycloak/index.d.ts.map +1 -0
- package/types/keycloak/keycloakWallet.d.ts +120 -0
- package/types/keycloak/keycloakWallet.d.ts.map +1 -0
- package/types/keycloak/mount.d.ts +16 -0
- package/types/keycloak/mount.d.ts.map +1 -0
- package/types/keycloak/mount.native.d.ts +5 -0
- package/types/keycloak/mount.native.d.ts.map +1 -0
- package/types/keycloak/registerWallet.d.ts +22 -0
- package/types/keycloak/registerWallet.d.ts.map +1 -0
- package/types/keycloak/types.d.ts +50 -0
- package/types/keycloak/types.d.ts.map +1 -0
- package/types/keycloak/useDirectLogin.d.ts +29 -0
- package/types/keycloak/useDirectLogin.d.ts.map +1 -0
- package/types/keycloak/useWalletLogin.d.ts +26 -0
- package/types/keycloak/useWalletLogin.d.ts.map +1 -0
- package/types/keycloak/useWalletLogin.native.d.ts +4 -0
- package/types/keycloak/useWalletLogin.native.d.ts.map +1 -0
- package/types/message/buildWalletMessage.d.ts +46 -0
- package/types/message/buildWalletMessage.d.ts.map +1 -0
- package/types/message/index.d.ts +2 -0
- package/types/message/index.d.ts.map +1 -0
- package/types/providers/WalletProvider.d.ts +8 -1
- package/types/providers/WalletProvider.d.ts.map +1 -1
- package/types/siws/message.d.ts.map +1 -1
- package/types/solana.d.ts +1 -1
- package/types/solana.d.ts.map +1 -1
- package/dist/cjs/connectkit/adapters/messages.cjs +0 -73
- package/dist/cjs/connectkit/adapters/messages.native.js +0 -79
- package/dist/cjs/connectkit/adapters/messages.native.js.map +0 -1
- package/dist/esm/connectkit/adapters/messages.mjs +0 -48
- package/dist/esm/connectkit/adapters/messages.mjs.map +0 -1
- package/dist/esm/connectkit/adapters/messages.native.js +0 -51
- package/dist/esm/connectkit/adapters/messages.native.js.map +0 -1
- package/dist/jsx/connectkit/adapters/messages.mjs +0 -48
- package/dist/jsx/connectkit/adapters/messages.mjs.map +0 -1
- package/dist/jsx/connectkit/adapters/messages.native.js +0 -79
- package/dist/jsx/connectkit/adapters/messages.native.js.map +0 -1
- package/src/connectkit/adapters/messages.ts +0 -84
- package/types/connectkit/adapters/messages.d.ts +0 -16
- package/types/connectkit/adapters/messages.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -22,8 +22,10 @@ Peers: `react`, `@tanstack/react-query`.
|
|
|
22
22
|
`useBalance`, `useConnect`, `useSignMessage`, `useSwitchChain`, …)
|
|
23
23
|
- **Components** — `ConnectKitButton`, `WalletButton`, `WalletAvatar`,
|
|
24
24
|
`WalletStatus`, `NetworkSwitcher`
|
|
25
|
-
- **Subpaths** — `./native` (WebView wallet bridge), `./connectkit`,
|
|
26
|
-
|
|
25
|
+
- **Subpaths** — `./native` (WebView wallet bridge), `./connectkit`,
|
|
26
|
+
`./message` (the wallet sign-in messages), `./keycloak` (wallet login
|
|
27
|
+
against the Keycloak wallet SPI), `./vite` and `./esbuild` (bundler
|
|
28
|
+
plugins, below)
|
|
27
29
|
|
|
28
30
|
## Per-chain clients
|
|
29
31
|
|
|
@@ -34,12 +36,12 @@ or a provider's `request`. Every client shares
|
|
|
34
36
|
verifiers take: EVM 0x hex, Solana base58, Sui base64, Bitcoin base64 BIP-322,
|
|
35
37
|
GPG the armored signature plus the armored public key.
|
|
36
38
|
|
|
37
|
-
| Hook / factory | What it adds
|
|
38
|
-
| --------------------------------------------------------------------------------- |
|
|
39
|
-
| `useEvmClient()` / `evmClient({ walletClient } \| { provider, account, chain })` | viem `walletClient` + `publicClient`, `sendTransaction`, `writeContract`, `waitForTransactionReceipt`, `erc20.{transfer, approve, ensureAllowance, balanceOf, allowance, decimals}`, safe for tokens like USDT that return nothing or refuse a non-zero to non-zero approve |
|
|
40
|
-
| `useSolanaClient()` / `solanaClient({ signer, rpcUrl?, chain? })` from `./solana` | web only: `@solana/kit` `rpc`, `signTransaction`, `signAndSendTransaction`, `sendTransaction`, `confirmTransaction`, `getBalance`, `transferSol`
|
|
41
|
-
| `useGpgSigner()` / `createGpgSigner()` | the copy/paste GPG signer: `signMessage` parks a request with its terminal `command`; `submit(pasted, request)` resolves it only while `request` is still the pending one and the pasted signature covers its message
|
|
42
|
-
| `useSuiClient()`, `useBitcoinClient()` | message signing only; `signTransaction` / `signPsbt` reject with `UnsupportedClientMethodError` until transactions land
|
|
39
|
+
| Hook / factory | What it adds |
|
|
40
|
+
| --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
41
|
+
| `useEvmClient()` / `evmClient({ walletClient } \| { provider, account, chain })` | viem `walletClient` + `publicClient`, `sendTransaction`, `writeContract`, `waitForTransactionReceipt`, `confirmTransaction`, `explorerTxUrl`, `erc20.{transfer, approve, ensureAllowance, balanceOf, allowance, decimals}`, safe for tokens like USDT that return nothing or refuse a non-zero to non-zero approve |
|
|
42
|
+
| `useSolanaClient()` / `solanaClient({ signer, rpcUrl?, chain? })` from `./solana` | web only: `@solana/kit` `rpc`, `signTransaction`, `signAndSendTransaction`, `sendTransaction`, `confirmTransaction`, `getBalance`, `transferSol` |
|
|
43
|
+
| `useGpgSigner()` / `createGpgSigner()` | the copy/paste GPG signer: `signMessage` parks a request with its terminal `command`; `submit(pasted, request)` resolves it only while `request` is still the pending one and the pasted signature covers its message |
|
|
44
|
+
| `useSuiClient()`, `useBitcoinClient()` | message signing only; `signTransaction` / `signPsbt` reject with `UnsupportedClientMethodError` until transactions land |
|
|
43
45
|
|
|
44
46
|
```tsx
|
|
45
47
|
import { useEvmClient } from "@multiplatform.one/web3";
|
|
@@ -56,9 +58,13 @@ await sol?.confirmTransaction(sent!);
|
|
|
56
58
|
|
|
57
59
|
`transferSol` returns the signature with the blockhash's `lastValidBlockHeight`,
|
|
58
60
|
and `confirmTransaction` waits until the chain passes that height before it
|
|
59
|
-
gives up. Its `SolanaConfirmationError` has
|
|
60
|
-
|
|
61
|
-
no `lastValidBlockHeight` to judge by)
|
|
61
|
+
gives up. Its `SolanaConfirmationError` has a `reason` and `mayStillLand`.
|
|
62
|
+
`failed` and `expired` set it `false`: the transaction will not land and a retry
|
|
63
|
+
is safe. `timeout` (there was no `lastValidBlockHeight` to judge by) and `rpc`
|
|
64
|
+
(a `getSignatureStatuses` or `getBlockHeight` poll failed, with the transport
|
|
65
|
+
error as `cause`) set it `true`: resending can pay twice, so call
|
|
66
|
+
`confirmTransaction` again on the same submission instead. `lastStatus` is the
|
|
67
|
+
last `confirmationStatus` the poll saw, `null` when it never saw the signature.
|
|
62
68
|
|
|
63
69
|
A Solana client runs over a byte-level `SolanaSigner`: `standardSolanaSigner`
|
|
64
70
|
(Wallet Standard: Phantom, Solflare, Backpack), `injectedSolanaSigner` (what the
|
|
@@ -72,6 +78,107 @@ signs Solana through the WebView bridge, so the root never imports
|
|
|
72
78
|
through `solanaMessageClient`, so a login page carries no kit.
|
|
73
79
|
`tests/clients/importGraph.spec.ts` holds both lines, for native and web.
|
|
74
80
|
|
|
81
|
+
### EVM payments: pending is not failed
|
|
82
|
+
|
|
83
|
+
A sent transaction can sit in the mempool for minutes and still land. viem's
|
|
84
|
+
receipt wait gives up after 180 s with an error that reads like a failure, so
|
|
85
|
+
an app would offer "try again" and the user would pay twice. The EVM client
|
|
86
|
+
never does that:
|
|
87
|
+
|
|
88
|
+
- `waitForTransactionReceipt(hash)` and `confirmTransaction(hash)` wait with
|
|
89
|
+
no timeout. Pass `{ timeout }` for a deadline, which holds even while an
|
|
90
|
+
RPC call hangs. Running out rejects with
|
|
91
|
+
`ReceiptPendingError` (`hash`, `chainId`, `explorerUrl`, `nonce` once the
|
|
92
|
+
node has seen it, `mayStillLand: true`), never a generic error. A failing
|
|
93
|
+
RPC never ends the wait: it backs off, up to 30 s between polls, and
|
|
94
|
+
retries.
|
|
95
|
+
- Pass `{ signal }` and abort it when the screen unmounts. A wait with no
|
|
96
|
+
timeout otherwise polls until the transaction settles, which for a dropped
|
|
97
|
+
one is forever. It rejects with `ReceiptWaitAbortedError`, an `AbortError`.
|
|
98
|
+
- `confirmTransaction` also rejects a reverted receipt with
|
|
99
|
+
`TransactionRevertedError`. `waitForTransactionReceipt` still resolves it,
|
|
100
|
+
as it always has.
|
|
101
|
+
- When another transaction takes the nonce, both reject with
|
|
102
|
+
`TransactionReplacedError`: `reason` is `cancelled` (the wallet's cancel),
|
|
103
|
+
`replaced` (a different transaction), or `unmatched` (the original was never
|
|
104
|
+
seen, so it could be a speed-up). A speed-up resolves with the faster copy's
|
|
105
|
+
receipt. The wait binary-searches the nonce's block however long it slept,
|
|
106
|
+
so resuming one far back needs an RPC that serves state that old, an
|
|
107
|
+
archive node. A chain's default public RPC often keeps only the last 128
|
|
108
|
+
blocks, and past that the wait stays pending.
|
|
109
|
+
- `erc20.ensureAllowance` confirms its approvals the same way and takes the
|
|
110
|
+
same `timeout` and `signal`. `erc20.transfer` and `approve` return once the wallet sends,
|
|
111
|
+
and their rejections classify too.
|
|
112
|
+
|
|
113
|
+
`classifyTxError(error)` sorts any of these, and viem's own errors, into what
|
|
114
|
+
to show:
|
|
115
|
+
|
|
116
|
+
| Kind | Meaning | Show |
|
|
117
|
+
| --------------- | --------------------------------------------------------------------- | ---------------------------------------------- |
|
|
118
|
+
| `user-rejected` | declined or cancelled in the wallet; nothing paid | "Cancelled", buy enabled |
|
|
119
|
+
| `reverted` | the contract refused it, in simulation or on chain; nothing paid | the reason, buy enabled |
|
|
120
|
+
| `pending` | sent with no receipt yet, or its nonce went to what may be a speed-up | "Pending" with the explorer link, buy disabled |
|
|
121
|
+
| `aborted` | the app stopped waiting; the transaction is as pending as it was | nothing; keep the pending hash |
|
|
122
|
+
| `failed` | anything else: no hash came back, or a different transaction won | the error, buy enabled |
|
|
123
|
+
|
|
124
|
+
`failed` with no hash usually means nothing was sent. A wallet that errors
|
|
125
|
+
after the user approved may still have broadcast it, so tell the user to
|
|
126
|
+
check the wallet's activity before paying again.
|
|
127
|
+
|
|
128
|
+
Persist the hash from the moment the wallet returns it, and what `onTrack`
|
|
129
|
+
hands over: the nonce once the node has seen the transaction, and a speed-up's
|
|
130
|
+
hash when one replaces it. A wait resumed after a reload needs the nonce to
|
|
131
|
+
notice a cancel or replacement that landed while the app was closed; without
|
|
132
|
+
it, a transaction the node has forgotten stays pending. A speed-up the page
|
|
133
|
+
never saw can't be told from a replacement after a reload, so it stays
|
|
134
|
+
pending too. Refuse a second buy while one is pending:
|
|
135
|
+
|
|
136
|
+
```tsx
|
|
137
|
+
import { classifyTxError, useEvmClient } from "@multiplatform.one/web3";
|
|
138
|
+
|
|
139
|
+
const evm = useEvmClient();
|
|
140
|
+
const [pending, setPending] = usePersistedState<{ hash: Hash; nonce?: number } | null>("buy", null);
|
|
141
|
+
|
|
142
|
+
async function settle(hash: Hash, nonce: number | undefined, signal: AbortSignal) {
|
|
143
|
+
try {
|
|
144
|
+
await evm.confirmTransaction(hash, { nonce, signal, onTrack: setPending });
|
|
145
|
+
setPending(null);
|
|
146
|
+
showPaid();
|
|
147
|
+
} catch (error) {
|
|
148
|
+
const kind = classifyTxError(error);
|
|
149
|
+
// aborted: unmounted, and the next mount resumes it. pending: superseded,
|
|
150
|
+
// maybe sped up, so keep it locked.
|
|
151
|
+
if (kind === "aborted" || kind === "pending") return;
|
|
152
|
+
setPending(null);
|
|
153
|
+
showFailure(kind, error);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Every wait belongs to a mounted screen, and stops when it unmounts.
|
|
158
|
+
useEffect(() => {
|
|
159
|
+
if (!evm || !pending) return;
|
|
160
|
+
const controller = new AbortController();
|
|
161
|
+
void settle(pending.hash, pending.nonce, controller.signal);
|
|
162
|
+
return () => controller.abort();
|
|
163
|
+
}, [evm, pending?.hash]);
|
|
164
|
+
|
|
165
|
+
async function buy() {
|
|
166
|
+
if (!evm || pending) return;
|
|
167
|
+
try {
|
|
168
|
+
await evm.erc20.ensureAllowance({ token: qcc, spender: store, amount });
|
|
169
|
+
const hash = await evm.writeContract({ address: store, abi, functionName: "buy", args: [id] });
|
|
170
|
+
setPending({ hash });
|
|
171
|
+
} catch (error) {
|
|
172
|
+
showFailure(classifyTxError(error), error);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
While `pending` is set, disable buy and link `evm.explorerTxUrl(pending.hash)`.
|
|
178
|
+
The effect settles it on mount and after a reload; a "check again" button can
|
|
179
|
+
remount it. When the client can't settle it, your backend, which sees the
|
|
180
|
+
purchase land, is the one to release the lock.
|
|
181
|
+
|
|
75
182
|
### Solana on mobile (follow-up)
|
|
76
183
|
|
|
77
184
|
The web path is done. A native app cannot reach Phantom through the WebView
|
|
@@ -155,6 +262,38 @@ export function App() {
|
|
|
155
262
|
}
|
|
156
263
|
```
|
|
157
264
|
|
|
265
|
+
## Keycloak direct login and onboarding
|
|
266
|
+
|
|
267
|
+
`kc.directGrant({ clientId })` trades a wallet signature for tokens without a
|
|
268
|
+
browser. A brand-new user, or a realm that later adds a required profile
|
|
269
|
+
field, has required actions Keycloak cannot run on a direct grant: it answers
|
|
270
|
+
400 "Account is not fully set up", which `login()` rejects as
|
|
271
|
+
`ProfileIncompleteError` and `useDirectLogin` reports as
|
|
272
|
+
`status: "profile-incomplete"`. Open the `url` of `kc.browserLoginUrl({
|
|
273
|
+
clientId, redirectUri })` in the system browser or a WebView page, where
|
|
274
|
+
Keycloak shows its own onboarding form, then call `signIn()` again once it
|
|
275
|
+
redirects back.
|
|
276
|
+
|
|
277
|
+
`browserLoginUrl` draws a fresh `state` and a PKCE S256 verifier on every call
|
|
278
|
+
and returns `{ url, state, codeVerifier }`. Keep `state` and accept the
|
|
279
|
+
redirect only when it matches; `codeVerifier` is for a caller that exchanges
|
|
280
|
+
the code at `kc.tokenUrl`. The client needs `standardFlowEnabled` and the
|
|
281
|
+
redirect URI registered exactly, with no wildcard. A direct-grant client does
|
|
282
|
+
not qualify: mpo's `siwe-public` and `siws-public` have `standardFlowEnabled:
|
|
283
|
+
false`, so mpo's realm carries a separate public client, `wallet-onboarding`
|
|
284
|
+
(PKCE S256 required, redirect URIs `http://localhost:8000/wallet`,
|
|
285
|
+
`http://localhost:3456/wallet` and `multiplatform-one://wallet`). The wallet
|
|
286
|
+
screen in `features/wallet` takes this path on its own.
|
|
287
|
+
|
|
288
|
+
```tsx
|
|
289
|
+
const { status, signIn } = useDirectLogin(grant);
|
|
290
|
+
if (status === "profile-incomplete") {
|
|
291
|
+
const login = kc.browserLoginUrl({ clientId: "wallet-onboarding", redirectUri: "app://auth" });
|
|
292
|
+
await saveState(login.state);
|
|
293
|
+
await Linking.openURL(login.url);
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
158
297
|
## License
|
|
159
298
|
|
|
160
299
|
Apache-2.0
|
package/dist/cjs/clients/evm.cjs
CHANGED
|
@@ -22,10 +22,98 @@ var __toCommonJS = mod => __copyProps(__defProp({}, "__esModule", {
|
|
|
22
22
|
}), mod);
|
|
23
23
|
var evm_exports = {};
|
|
24
24
|
__export(evm_exports, {
|
|
25
|
+
ReceiptPendingError: () => ReceiptPendingError,
|
|
26
|
+
ReceiptWaitAbortedError: () => ReceiptWaitAbortedError,
|
|
27
|
+
TransactionReplacedError: () => TransactionReplacedError,
|
|
28
|
+
TransactionRevertedError: () => TransactionRevertedError,
|
|
29
|
+
classifyTxError: () => classifyTxError,
|
|
25
30
|
evmClient: () => evmClient
|
|
26
31
|
});
|
|
27
32
|
module.exports = __toCommonJS(evm_exports);
|
|
28
33
|
var import_viem = require("viem");
|
|
34
|
+
class ReceiptPendingError extends Error {
|
|
35
|
+
constructor(hash, chainId, options) {
|
|
36
|
+
super(`${options.label ?? "transaction"} ${hash} on chain ${chainId} ${options.detail}; it may still land, so do not resend it`, {
|
|
37
|
+
cause: options.cause
|
|
38
|
+
});
|
|
39
|
+
this.hash = hash;
|
|
40
|
+
this.chainId = chainId;
|
|
41
|
+
this.name = "ReceiptPendingError";
|
|
42
|
+
this.explorerUrl = options.explorerUrl;
|
|
43
|
+
this.nonce = options.nonce;
|
|
44
|
+
}
|
|
45
|
+
hash;
|
|
46
|
+
chainId;
|
|
47
|
+
mayStillLand = true;
|
|
48
|
+
explorerUrl;
|
|
49
|
+
/** Known once the node has seen the transaction. Keep it with the hash to resume the wait. */
|
|
50
|
+
nonce;
|
|
51
|
+
}
|
|
52
|
+
class ReceiptWaitAbortedError extends Error {
|
|
53
|
+
constructor(hash, chainId, nonce, cause) {
|
|
54
|
+
super(`stopped waiting for transaction ${hash} on chain ${chainId}`, {
|
|
55
|
+
cause
|
|
56
|
+
});
|
|
57
|
+
this.hash = hash;
|
|
58
|
+
this.chainId = chainId;
|
|
59
|
+
this.nonce = nonce;
|
|
60
|
+
this.name = "AbortError";
|
|
61
|
+
}
|
|
62
|
+
hash;
|
|
63
|
+
chainId;
|
|
64
|
+
nonce;
|
|
65
|
+
mayStillLand = true;
|
|
66
|
+
}
|
|
67
|
+
class TransactionRevertedError extends Error {
|
|
68
|
+
constructor(receipt, chainId, label = "transaction") {
|
|
69
|
+
super(`${label} ${receipt.transactionHash} reverted in block ${receipt.blockNumber}`);
|
|
70
|
+
this.receipt = receipt;
|
|
71
|
+
this.chainId = chainId;
|
|
72
|
+
this.name = "TransactionRevertedError";
|
|
73
|
+
this.hash = receipt.transactionHash;
|
|
74
|
+
}
|
|
75
|
+
receipt;
|
|
76
|
+
chainId;
|
|
77
|
+
mayStillLand = false;
|
|
78
|
+
hash;
|
|
79
|
+
}
|
|
80
|
+
class TransactionReplacedError extends Error {
|
|
81
|
+
constructor(hash, chainId, reason, replacement) {
|
|
82
|
+
super(`transaction ${hash} was ${reason === "unmatched" ? "superseded" : reason}${replacement ? ` by ${replacement.transactionHash}` : ""}: another transaction took its nonce`);
|
|
83
|
+
this.hash = hash;
|
|
84
|
+
this.chainId = chainId;
|
|
85
|
+
this.reason = reason;
|
|
86
|
+
this.replacement = replacement;
|
|
87
|
+
this.name = "TransactionReplacedError";
|
|
88
|
+
}
|
|
89
|
+
hash;
|
|
90
|
+
chainId;
|
|
91
|
+
reason;
|
|
92
|
+
replacement;
|
|
93
|
+
mayStillLand = false;
|
|
94
|
+
}
|
|
95
|
+
function causes(error) {
|
|
96
|
+
const chain = [];
|
|
97
|
+
for (let e = error; e && typeof e === "object" && chain.length < 16; e = e.cause) {
|
|
98
|
+
if (chain.includes(e)) break;
|
|
99
|
+
chain.push(e);
|
|
100
|
+
}
|
|
101
|
+
return chain;
|
|
102
|
+
}
|
|
103
|
+
function classifyTxError(error) {
|
|
104
|
+
const chain = causes(error);
|
|
105
|
+
const pendingAt = chain.findIndex(e => e.mayStillLand === true || e.name === "WaitForTransactionReceiptTimeoutError" || e.name === "TransactionReplacedError" && e.reason === "unmatched");
|
|
106
|
+
const abortedAt = chain.findIndex(e => e.name === "AbortError" && typeof e.mayStillLand === "boolean");
|
|
107
|
+
if (abortedAt >= 0 && (pendingAt < 0 || abortedAt <= pendingAt)) return "aborted";
|
|
108
|
+
if (pendingAt >= 0) return "pending";
|
|
109
|
+
if (chain.some(e => e.name === "UserRejectedRequestError" || e.code === 4001 || e.name === "TransactionReplacedError" && e.reason === "cancelled")) {
|
|
110
|
+
return "user-rejected";
|
|
111
|
+
}
|
|
112
|
+
if (chain.some(e => e.name === "ContractFunctionRevertedError" || e.name === "ExecutionRevertedError" || e.receipt?.status === "reverted")) {
|
|
113
|
+
return "reverted";
|
|
114
|
+
}
|
|
115
|
+
return "failed";
|
|
116
|
+
}
|
|
29
117
|
const erc20WriteAbi = [{
|
|
30
118
|
type: "function",
|
|
31
119
|
name: "transfer",
|
|
@@ -86,16 +174,208 @@ function evmClient(config) {
|
|
|
86
174
|
chain
|
|
87
175
|
});
|
|
88
176
|
};
|
|
89
|
-
const
|
|
177
|
+
const explorer = chain.blockExplorers?.default.url.replace(/\/+$/, "");
|
|
178
|
+
const explorerTxUrl = hash => explorer ? `${explorer}/tx/${hash}` : void 0;
|
|
179
|
+
const missing = name => error => {
|
|
180
|
+
if (error?.name === name) return void 0;
|
|
181
|
+
throw error;
|
|
182
|
+
};
|
|
183
|
+
const getReceipt = hash => publicClient.getTransactionReceipt({
|
|
184
|
+
hash
|
|
185
|
+
}).catch(missing("TransactionReceiptNotFoundError"));
|
|
186
|
+
const getTransaction = hash => publicClient.getTransaction({
|
|
187
|
+
hash
|
|
188
|
+
}).catch(missing("TransactionNotFoundError"));
|
|
189
|
+
const sameAddress = (a, b) => !!a && !!b && a.toLowerCase() === b.toLowerCase();
|
|
190
|
+
const nonceBlock = async (count, nonce, taken, free) => {
|
|
191
|
+
let hi = taken;
|
|
192
|
+
let lo = -1n;
|
|
193
|
+
if (free !== void 0 && free < hi) lo = free;else {
|
|
194
|
+
for (let step = 1n; hi - step >= 0n; step *= 2n) {
|
|
195
|
+
const probe = hi - step;
|
|
196
|
+
if ((await count(probe)) <= nonce) {
|
|
197
|
+
lo = probe;
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
hi = probe;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
while (hi - lo > 1n) {
|
|
204
|
+
const mid = (lo + hi) / 2n;
|
|
205
|
+
if ((await count(mid)) > nonce) hi = mid;else lo = mid;
|
|
206
|
+
}
|
|
207
|
+
return {
|
|
208
|
+
block: hi,
|
|
209
|
+
free: lo >= 0n ? lo : void 0
|
|
210
|
+
};
|
|
211
|
+
};
|
|
212
|
+
const replacementReason = (taker, original) => {
|
|
213
|
+
if (original && sameAddress(taker.to, original.to) && taker.value === original.value && taker.input === original.input) {
|
|
214
|
+
return "repriced";
|
|
215
|
+
}
|
|
216
|
+
if (sameAddress(taker.to, taker.from) && taker.value === 0n) return "cancelled";
|
|
217
|
+
return original ? "replaced" : "unmatched";
|
|
218
|
+
};
|
|
219
|
+
const after = (ms, fire) => {
|
|
220
|
+
const step = 2 ** 31 - 1;
|
|
221
|
+
let cancel = () => {};
|
|
222
|
+
const arm = left => {
|
|
223
|
+
const id = setTimeout(() => left > step ? arm(left - step) : fire(), Math.min(left, step));
|
|
224
|
+
cancel = () => clearTimeout(id);
|
|
225
|
+
};
|
|
226
|
+
arm(Math.max(0, ms));
|
|
227
|
+
return () => cancel();
|
|
228
|
+
};
|
|
229
|
+
const backoff = failures => {
|
|
230
|
+
const ceiling = Math.min(3e4, Math.max(1, publicClient.pollingInterval) * 2 ** Math.min(failures - 1, 16));
|
|
231
|
+
return ceiling / 2 + Math.random() * ceiling / 2;
|
|
232
|
+
};
|
|
233
|
+
const waitForReceipt = async (hash, options = {}, label) => {
|
|
234
|
+
const {
|
|
235
|
+
signal
|
|
236
|
+
} = options;
|
|
237
|
+
const confirmations = BigInt(Math.max(1, options.confirmations ?? 1));
|
|
238
|
+
const {
|
|
239
|
+
timeout
|
|
240
|
+
} = options;
|
|
241
|
+
const deadline = timeout !== void 0 && Number.isFinite(timeout) && timeout > 0 ? Date.now() + timeout : void 0;
|
|
242
|
+
let target = hash;
|
|
243
|
+
let original;
|
|
244
|
+
let nonce = options.nonce;
|
|
245
|
+
let freeBlock;
|
|
246
|
+
let failures = 0;
|
|
247
|
+
let lastError;
|
|
248
|
+
const aborted = () => new ReceiptWaitAbortedError(hash, chain.id, nonce, signal?.reason);
|
|
249
|
+
const timedOut = () => new ReceiptPendingError(hash, chain.id, {
|
|
250
|
+
detail: `has no receipt after ${timeout} ms`,
|
|
251
|
+
explorerUrl: explorerTxUrl(hash),
|
|
252
|
+
nonce,
|
|
253
|
+
cause: lastError,
|
|
254
|
+
label
|
|
255
|
+
});
|
|
256
|
+
let ended = false;
|
|
257
|
+
const track = () => {
|
|
258
|
+
if (ended || signal?.aborted || nonce === void 0) return;
|
|
259
|
+
try {
|
|
260
|
+
options.onTrack?.({
|
|
261
|
+
hash: target,
|
|
262
|
+
nonce
|
|
263
|
+
});
|
|
264
|
+
} catch {}
|
|
265
|
+
};
|
|
266
|
+
if (signal?.aborted) throw aborted();
|
|
267
|
+
const guard = (promise, onStop = () => {}) => new Promise((resolve, reject) => {
|
|
268
|
+
let cancelTimer = () => {};
|
|
269
|
+
const cleanup = () => {
|
|
270
|
+
cancelTimer();
|
|
271
|
+
signal?.removeEventListener("abort", stop);
|
|
272
|
+
};
|
|
273
|
+
const give = error => {
|
|
274
|
+
cleanup();
|
|
275
|
+
onStop();
|
|
276
|
+
reject(error);
|
|
277
|
+
};
|
|
278
|
+
const stop = () => give(aborted());
|
|
279
|
+
promise.then(resolve, reject).finally(cleanup);
|
|
280
|
+
if (signal?.aborted) return stop();
|
|
281
|
+
signal?.addEventListener("abort", stop, {
|
|
282
|
+
once: true
|
|
283
|
+
});
|
|
284
|
+
if (deadline !== void 0) cancelTimer = after(deadline - Date.now(), () => give(timedOut()));
|
|
285
|
+
});
|
|
286
|
+
const rpc = call => {
|
|
287
|
+
if (ended || signal?.aborted) throw aborted();
|
|
288
|
+
return call();
|
|
289
|
+
};
|
|
290
|
+
const countAt = (address, blockNumber) => rpc(() => publicClient.getTransactionCount({
|
|
291
|
+
address,
|
|
292
|
+
blockNumber
|
|
293
|
+
}));
|
|
294
|
+
const poll = async () => {
|
|
295
|
+
const head = await rpc(() => publicClient.getBlockNumber({
|
|
296
|
+
cacheTime: 0
|
|
297
|
+
}));
|
|
298
|
+
const receipt = await rpc(() => getReceipt(target));
|
|
299
|
+
if (receipt) return head - receipt.blockNumber + 1n >= confirmations ? receipt : void 0;
|
|
300
|
+
if (!original) {
|
|
301
|
+
original = await rpc(() => getTransaction(target));
|
|
302
|
+
if (original && original.nonce !== nonce) {
|
|
303
|
+
nonce = original.nonce;
|
|
304
|
+
track();
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
if (nonce === void 0) return void 0;
|
|
308
|
+
const from = original?.from ?? account.address;
|
|
309
|
+
if ((await countAt(from, head)) <= nonce) {
|
|
310
|
+
freeBlock = head;
|
|
311
|
+
return void 0;
|
|
312
|
+
}
|
|
313
|
+
const found = await nonceBlock(n => countAt(from, n), nonce, head, freeBlock);
|
|
314
|
+
freeBlock = found.free;
|
|
315
|
+
const block = await rpc(() => publicClient.getBlock({
|
|
316
|
+
blockNumber: found.block,
|
|
317
|
+
includeTransactions: true
|
|
318
|
+
}));
|
|
319
|
+
const taker = block.transactions.find(tx => sameAddress(tx.from, from) && tx.nonce === nonce);
|
|
320
|
+
if (!taker || taker.hash === target) {
|
|
321
|
+
if (!taker) freeBlock = void 0;
|
|
322
|
+
return void 0;
|
|
323
|
+
}
|
|
324
|
+
const reason = replacementReason(taker, original);
|
|
325
|
+
if (reason === "repriced") {
|
|
326
|
+
target = taker.hash;
|
|
327
|
+
original = taker;
|
|
328
|
+
track();
|
|
329
|
+
return void 0;
|
|
330
|
+
}
|
|
331
|
+
if (head - found.block + 1n < confirmations) return void 0;
|
|
332
|
+
const replacement = await rpc(() => getReceipt(taker.hash));
|
|
333
|
+
throw new TransactionReplacedError(hash, chain.id, reason, replacement);
|
|
334
|
+
};
|
|
335
|
+
try {
|
|
336
|
+
for (;;) {
|
|
337
|
+
try {
|
|
338
|
+
const receipt = await guard(poll());
|
|
339
|
+
if (receipt) return receipt;
|
|
340
|
+
failures = 0;
|
|
341
|
+
} catch (error) {
|
|
342
|
+
if (error instanceof TransactionReplacedError || error instanceof ReceiptWaitAbortedError || error instanceof ReceiptPendingError) {
|
|
343
|
+
throw error;
|
|
344
|
+
}
|
|
345
|
+
lastError = error;
|
|
346
|
+
failures++;
|
|
347
|
+
}
|
|
348
|
+
if (deadline !== void 0 && Date.now() >= deadline) throw timedOut();
|
|
349
|
+
const pause = failures ? backoff(failures) : publicClient.pollingInterval;
|
|
350
|
+
let wake = () => {};
|
|
351
|
+
await guard(new Promise(resolve => {
|
|
352
|
+
wake = after(pause, () => resolve(void 0));
|
|
353
|
+
}), () => wake());
|
|
354
|
+
}
|
|
355
|
+
} finally {
|
|
356
|
+
ended = true;
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
const confirmTransaction = async (hash, options, label) => {
|
|
360
|
+
const receipt = await waitForReceipt(hash, options, label);
|
|
361
|
+
if (receipt.status !== "success") throw new TransactionRevertedError(receipt, chain.id, label);
|
|
362
|
+
return receipt;
|
|
363
|
+
};
|
|
364
|
+
const approveAndWait = async (token, spender, amount, options) => {
|
|
365
|
+
if (options.signal?.aborted) {
|
|
366
|
+
throw Object.assign(new Error("stopped before sending the approval", {
|
|
367
|
+
cause: options.signal.reason
|
|
368
|
+
}), {
|
|
369
|
+
name: "AbortError",
|
|
370
|
+
mayStillLand: false
|
|
371
|
+
});
|
|
372
|
+
}
|
|
90
373
|
const hash = await erc20.approve({
|
|
91
374
|
token,
|
|
92
375
|
spender,
|
|
93
376
|
amount
|
|
94
377
|
});
|
|
95
|
-
|
|
96
|
-
hash
|
|
97
|
-
});
|
|
98
|
-
if (receipt.status !== "success") throw new Error(`approve ${hash} reverted`);
|
|
378
|
+
await confirmTransaction(hash, options, "approve");
|
|
99
379
|
return hash;
|
|
100
380
|
};
|
|
101
381
|
const erc20 = {
|
|
@@ -137,12 +417,20 @@ function evmClient(config) {
|
|
|
137
417
|
async ensureAllowance({
|
|
138
418
|
token,
|
|
139
419
|
spender,
|
|
140
|
-
amount
|
|
420
|
+
amount,
|
|
421
|
+
timeout,
|
|
422
|
+
signal
|
|
141
423
|
}) {
|
|
142
424
|
const current = await erc20.allowance(token, spender);
|
|
143
425
|
if (current >= amount) return null;
|
|
144
|
-
if (current > 0n) await approveAndWait(token, spender, 0n
|
|
145
|
-
|
|
426
|
+
if (current > 0n) await approveAndWait(token, spender, 0n, {
|
|
427
|
+
timeout,
|
|
428
|
+
signal
|
|
429
|
+
});
|
|
430
|
+
return approveAndWait(token, spender, amount, {
|
|
431
|
+
timeout,
|
|
432
|
+
signal
|
|
433
|
+
});
|
|
146
434
|
}
|
|
147
435
|
};
|
|
148
436
|
return {
|
|
@@ -163,9 +451,9 @@ function evmClient(config) {
|
|
|
163
451
|
...parameters
|
|
164
452
|
}),
|
|
165
453
|
writeContract,
|
|
166
|
-
waitForTransactionReceipt: hash =>
|
|
167
|
-
|
|
168
|
-
|
|
454
|
+
waitForTransactionReceipt: (hash, options) => waitForReceipt(hash, options),
|
|
455
|
+
confirmTransaction: (hash, options) => confirmTransaction(hash, options),
|
|
456
|
+
explorerTxUrl,
|
|
169
457
|
erc20
|
|
170
458
|
};
|
|
171
459
|
}
|