Skip to content

Commit

Permalink
feat: In-app wallets for react native v5 (#3064)
Browse files Browse the repository at this point in the history
  • Loading branch information
joaquim-verges committed May 21, 2024
1 parent dd1d22b commit f55fa4c
Show file tree
Hide file tree
Showing 59 changed files with 4,770 additions and 1,045 deletions.
6 changes: 6 additions & 0 deletions .changeset/tasty-mice-move.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@thirdweb-dev/react-native-adapter": minor
"thirdweb": minor
---

In-app wallet support for react native
27 changes: 15 additions & 12 deletions packages/react-native-adapter/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,26 +23,29 @@
},
"./package.json": "./package.json"
},
"files": [
"dist/*",
"src/*"
],
"files": ["dist/*", "src/*"],
"dependencies": {
"@walletconnect/react-native-compat": "^2.13.0"
"@aws-sdk/client-lambda": "3.577.0",
"@aws-sdk/credential-providers": "3.577.0",
"@walletconnect/react-native-compat": "2.13.0",
"amazon-cognito-identity-js": "6.3.12",
"aws-amplify": "5.3.18",
"shamir-secret-sharing": "0.0.3"
},
"peerDependencies": {
"typescript": ">=5.0.4",
"@react-native-async-storage/async-storage": "*",
"@react-native-community/netinfo": "*",
"expo-application": "*",
"react-native-get-random-values": "*"
"@react-native-async-storage/async-storage": "^1",
"@react-native-community/netinfo": "^11",
"expo-application": "^5",
"expo-web-browser": "^13",
"react-native-aes-gcm-crypto": "^0.2",
"react-native-quick-crypto": ">=0.6.1",
"shamir-secret-sharing": "^0.0.3",
"react-native-get-random-values": "^1"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
},
"expo-application": {
"optional": true
}
},
"scripts": {
Expand Down
45 changes: 45 additions & 0 deletions packages/thirdweb/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,17 @@
"viem": "2.10.9"
},
"peerDependencies": {
"@aws-sdk/client-lambda": "^3",
"@aws-sdk/credential-providers": "^3",
"@react-native-async-storage/async-storage": "^1",
"amazon-cognito-identity-js": "^6",
"aws-amplify": "^5",
"ethers": "^5 || ^6",
"expo-web-browser": "^13",
"react": ">=18",
"react-native-aes-gcm-crypto": "^0.2",
"react-native-quick-crypto": ">=0.6.1",
"shamir-secret-sharing": "^0.0.3",
"typescript": ">=5.0.4"
},
"peerDependenciesMeta": {
Expand All @@ -180,6 +189,33 @@
},
"typescript": {
"optional": true
},
"react-native-aes-gcm-crypto": {
"optional": true
},
"expo-web-browser": {
"optional": true
},
"react-native-quick-crypto": {
"optional": true
},
"shamir-secret-sharing": {
"optional": true
},
"aws-amplify": {
"optional": true
},
"@aws-sdk/client-lambda": {
"optional": true
},
"@aws-sdk/credential-providers": {
"optional": true
},
"amazon-cognito-identity-js": {
"optional": true
},
"@react-native-async-storage/async-storage": {
"optional": true
}
},
"scripts": {
Expand Down Expand Up @@ -210,14 +246,23 @@
"node": ">=18"
},
"devDependencies": {
"@aws-sdk/client-lambda": "3.577.0",
"@aws-sdk/credential-providers": "3.577.0",
"@react-native-async-storage/async-storage": "^1.23.1",
"@testing-library/jest-dom": "^6.4.5",
"@testing-library/react": "^15.0.6",
"@types/cross-spawn": "^6.0.6",
"@types/react": "18.2.17",
"@vitejs/plugin-react": "^4.2.1",
"@vitest/ui": "1.5.0",
"amazon-cognito-identity-js": "6.3.12",
"aws-amplify": "5.3.18",
"cross-spawn": "7.0.3",
"expo-web-browser": "13.0.3",
"happy-dom": "^14.10.1",
"react-native-aes-gcm-crypto": "0.2.2",
"react-native-quick-crypto": "0.7.0-rc.6",
"shamir-secret-sharing": "0.0.3",
"vitest": "1.5.0"
}
}
4 changes: 2 additions & 2 deletions packages/thirdweb/src/auth/core/generate-login-payload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ export function generateLoginPayload(options: AuthOptions) {
nonce: await (
options.login?.nonce?.generate ||
(() =>
import("../../utils/random.js").then(({ randomBytes }) =>
randomBytes(),
import("../../utils/random.js").then(({ randomBytesHex }) =>
randomBytesHex(),
))
)(),
statement: options.login?.statement || DEFAULT_LOGIN_STATEMENT,
Expand Down
2 changes: 1 addition & 1 deletion packages/thirdweb/src/exports/wallets/in-app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ export {
type GetAuthenticatedUserParams,
} from "../../wallets/in-app/core/authentication/index.js";

export { hasStoredPasskey } from "../../wallets/in-app/implementations/lib/auth/passkeys.js";
export { hasStoredPasskey } from "../../wallets/in-app/web/lib/auth/passkeys.js";
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { AbiParameterToPrimitiveType, Address } from "abitype";
import type { ThirdwebContract } from "../../../contract/contract.js";
import { dateToSeconds, tenYearsFromNow } from "../../../utils/date.js";
import type { Hex } from "../../../utils/encoding/hex.js";
import { randomBytes } from "../../../utils/random.js";
import { randomBytesHex } from "../../../utils/random.js";
import type { Account } from "../../../wallets/interfaces/wallet.js";
import { airdropERC1155WithSignature as generatedAirdropERC1155WithSignature } from "../__generated__/Airdrop/write/airdropERC1155WithSignature.js";

Expand Down Expand Up @@ -72,7 +72,7 @@ export async function generateAirdropSignatureERC1155(
const { airdropRequest, account, contract } = options;
const tokenAddress = airdropRequest.tokenAddress;
const contents = airdropRequest.contents;
const uid = airdropRequest.uid || (await randomBytes());
const uid = airdropRequest.uid || (await randomBytesHex());
const endTime = airdropRequest.expirationTimestamp || tenYearsFromNow();

const req: ReqType = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { AbiParameterToPrimitiveType, Address } from "abitype";
import type { ThirdwebContract } from "../../../contract/contract.js";
import { dateToSeconds, tenYearsFromNow } from "../../../utils/date.js";
import type { Hex } from "../../../utils/encoding/hex.js";
import { randomBytes } from "../../../utils/random.js";
import { randomBytesHex } from "../../../utils/random.js";
import type { Account } from "../../../wallets/interfaces/wallet.js";
import { airdropERC20WithSignature as generatedAirdropERC20WithSignature } from "../__generated__/Airdrop/write/airdropERC20WithSignature.js";

Expand Down Expand Up @@ -72,7 +72,7 @@ export async function generateAirdropSignatureERC20(
const { airdropRequest, account, contract } = options;
const tokenAddress = airdropRequest.tokenAddress;
const contents = airdropRequest.contents;
const uid = airdropRequest.uid || (await randomBytes());
const uid = airdropRequest.uid || (await randomBytesHex());

const endTime = airdropRequest.expirationTimestamp || tenYearsFromNow();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { AbiParameterToPrimitiveType, Address } from "abitype";
import type { ThirdwebContract } from "../../../contract/contract.js";
import { dateToSeconds, tenYearsFromNow } from "../../../utils/date.js";
import type { Hex } from "../../../utils/encoding/hex.js";
import { randomBytes } from "../../../utils/random.js";
import { randomBytesHex } from "../../../utils/random.js";
import type { Account } from "../../../wallets/interfaces/wallet.js";
import { airdropERC721WithSignature as generatedAirdropERC721WithSignature } from "../__generated__/Airdrop/write/airdropERC721WithSignature.js";

Expand Down Expand Up @@ -72,7 +72,7 @@ export async function generateAirdropSignatureERC721(
const { airdropRequest, account, contract } = options;
const tokenAddress = airdropRequest.tokenAddress;
const contents = airdropRequest.contents;
const uid = airdropRequest.uid || (await randomBytes());
const uid = airdropRequest.uid || (await randomBytesHex());
const endTime = airdropRequest.expirationTimestamp || tenYearsFromNow();

const req: ReqType = {
Expand Down
4 changes: 2 additions & 2 deletions packages/thirdweb/src/extensions/erc1155/write/sigMint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { toBigInt } from "../../../utils/bigint.js";
import { dateToSeconds, tenYearsFromNow } from "../../../utils/date.js";
import type { Hex } from "../../../utils/encoding/hex.js";
import type { NFTInput } from "../../../utils/nft/parseNft.js";
import { randomBytes } from "../../../utils/random.js";
import { randomBytesHex } from "../../../utils/random.js";
import type { Account } from "../../../wallets/interfaces/wallet.js";
import {
type MintWithSignatureParams,
Expand Down Expand Up @@ -130,7 +130,7 @@ export async function generateMintSignature(
return "";
})(),
// uid computation
mintRequest.uid || (await randomBytes()),
mintRequest.uid || (await randomBytesHex()),
]);

const startTime = mintRequest.validityStartTimestamp || new Date(0);
Expand Down
4 changes: 2 additions & 2 deletions packages/thirdweb/src/extensions/erc20/write/sigMint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type { ThirdwebContract } from "../../../contract/contract.js";
import type { BaseTransactionOptions } from "../../../transaction/types.js";
import { dateToSeconds, tenYearsFromNow } from "../../../utils/date.js";
import type { Hex } from "../../../utils/encoding/hex.js";
import { randomBytes } from "../../../utils/random.js";
import { randomBytesHex } from "../../../utils/random.js";
import type { Account } from "../../../wallets/interfaces/wallet.js";
import { name } from "../../common/read/name.js";
import {
Expand Down Expand Up @@ -125,7 +125,7 @@ export async function generateMintSignature(
});
})(),
// uid computation
mintRequest.uid || (await randomBytes()),
mintRequest.uid || (await randomBytesHex()),
// ERC20Permit (EIP-712) spec differs from signature mint 721, 1155.
// it uses the token name in the domain separator
name({
Expand Down
6 changes: 3 additions & 3 deletions packages/thirdweb/src/extensions/erc4337/account/common.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ADDRESS_ZERO } from "../../../constants/addresses.js";
import type { ThirdwebContract } from "../../../contract/contract.js";
import { dateToSeconds, tenYearsFromNow } from "../../../utils/date.js";
import { randomBytes } from "../../../utils/random.js";
import { randomBytesHex } from "../../../utils/random.js";
import { toWei } from "../../../utils/units.js";
import type { Account } from "../../../wallets/interfaces/wallet.js";
import type { SetPermissionsForSignerParams } from "../__generated__/IAccountPermissions/write/setPermissionsForSigner.js";
Expand Down Expand Up @@ -54,7 +54,7 @@ export async function toContractPermissions(options: {
),
reqValidityStartTimestamp: 0n,
reqValidityEndTimestamp: dateToSeconds(tenYearsFromNow()),
uid: await randomBytes(),
uid: await randomBytesHex(),
isAdmin: 0, // session key flag
signer: target,
};
Expand All @@ -75,7 +75,7 @@ export async function defaultPermissionsForAdmin(options: {
permissionEndTimestamp: 0n,
reqValidityStartTimestamp: 0n,
reqValidityEndTimestamp: dateToSeconds(tenYearsFromNow()),
uid: await randomBytes(),
uid: await randomBytesHex(),
isAdmin: action === "add-admin" ? 1 : action === "remove-admin" ? 2 : 0,
signer: target,
};
Expand Down
4 changes: 2 additions & 2 deletions packages/thirdweb/src/extensions/erc721/write/sigMint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import type { BaseTransactionOptions } from "../../../transaction/types.js";
import { toBigInt } from "../../../utils/bigint.js";
import { dateToSeconds, tenYearsFromNow } from "../../../utils/date.js";
import type { NFTInput } from "../../../utils/nft/parseNft.js";
import { randomBytes } from "../../../utils/random.js";
import { randomBytesHex } from "../../../utils/random.js";
import type { Account } from "../../../wallets/interfaces/wallet.js";
import {
type MintWithSignatureParams,
Expand Down Expand Up @@ -129,7 +129,7 @@ export async function generateMintSignature(
return "";
})(),
// uid computation
mintRequest.uid || (await randomBytes()),
mintRequest.uid || (await randomBytesHex()),
]);

const startTime = mintRequest.validityStartTimestamp || new Date(0);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";
import { useCallback, useEffect, useRef, useState } from "react";
import { preAuthenticate } from "../../../../wallets/in-app/core/authentication/index.js";
import type { SendEmailOtpReturnType } from "../../../../wallets/in-app/implementations/index.js";
import type { SendEmailOtpReturnType } from "../../../../wallets/in-app/core/authentication/type.js";
import type { Wallet } from "../../../../wallets/interfaces/wallet.js";
import { useConnectUI } from "../../../core/hooks/others/useWalletConnectionCtx.js";
import { FadeIn } from "../../ui/components/FadeIn.js";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useEffect, useRef, useState } from "react";
import type { Chain } from "../../../../chains/types.js";
import type { ThirdwebClient } from "../../../../client/client.js";
import type { Wallet } from "../../../../exports/wallets.js";
import { hasStoredPasskey } from "../../../../wallets/in-app/implementations/lib/auth/passkeys.js";
import { hasStoredPasskey } from "../../../../wallets/in-app/web/lib/auth/passkeys.js";
import { useConnectUI } from "../../../core/hooks/others/useWalletConnectionCtx.js";
import { AccentFailIcon } from "../../ui/ConnectWallet/icons/AccentFailIcon.js";
import { FingerPrintIcon } from "../../ui/ConnectWallet/icons/FingerPrintIcon.js";
Expand Down
11 changes: 6 additions & 5 deletions packages/thirdweb/src/utils/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,11 +126,12 @@ export function getPlatformHeaders() {
os = detectOS(navigator.userAgent);
}

const bundleId =
typeof globalThis !== "undefined" && "APP_BUNDLE_ID" in globalThis
? // biome-ignore lint/suspicious/noExplicitAny: get around globalThis typing
((globalThis as any).APP_BUNDLE_ID as string)
: undefined;
let bundleId: string | undefined = undefined;
if (typeof globalThis !== "undefined" && "Application" in globalThis) {
// shims use wallet connect RN module which injects Application info in globalThis
// biome-ignore lint/suspicious/noExplicitAny: get around globalThis typing
bundleId = (globalThis as any).Application.applicationId;
}

previousPlatform = Object.entries({
"x-sdk-platform": detectPlatform(),
Expand Down
4 changes: 2 additions & 2 deletions packages/thirdweb/src/utils/jwt/encode-jwt.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Account } from "../../wallets/interfaces/wallet.js";
import { stringToBytes } from "../encoding/to-bytes.js";
import { randomBytes } from "../random.js";
import { randomBytesHex } from "../random.js";
import { uint8ArrayToBase64 } from "../uint8-array.js";
import { PRECOMPILED_B64_ENCODED_JWT_HEADER } from "./jwt-header.js";
import type { JWTPayload } from "./types.js";
Expand Down Expand Up @@ -72,7 +72,7 @@ async function ensureJWTPayload(payload: JWTPayloadInput): Promise<JWTPayload> {
nbf: Math.floor(payload.nbf.getTime() / 1000),
iat: Math.floor(payload.iat.getTime() / 1000),
// default to uuid if jti is not provided
jti: payload.jti || (await randomBytes()),
jti: payload.jti || (await randomBytesHex()),
ctx: payload.ctx,
};
}
13 changes: 9 additions & 4 deletions packages/thirdweb/src/utils/random.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@ import { uint8ArrayToHex } from "./encoding/hex.js";
/**
* @internal
*/
export async function randomBytes(length = 32) {
return uint8ArrayToHex(
globalThis.crypto.getRandomValues(new Uint8Array(length)),
);
export function randomBytesHex(length = 32) {
return uint8ArrayToHex(randomBytesBuffer(length));
}

/**
* @internal
*/
export function randomBytesBuffer(length = 32) {
return globalThis.crypto.getRandomValues(new Uint8Array(length));
}
2 changes: 1 addition & 1 deletion packages/thirdweb/src/utils/uint8-array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export function areUint8ArraysEqual(a: Uint8Array, b: Uint8Array): boolean {
* //=> 'Hello'
* ```
*/
function uint8ArrayToString(array: Uint8Array): string {
export function uint8ArrayToString(array: Uint8Array): string {
assertUint8Array(array);
return cachedTextDecoder().decode(array);
}
Expand Down
2 changes: 1 addition & 1 deletion packages/thirdweb/src/wallets/eip5792/get-calls-status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export async function getCallsStatus({
// These conveniently operate the same
if (isSmartWallet(wallet) || isInAppWallet(wallet)) {
const { inAppWalletGetCallsStatus } = await import(
"../in-app/core/lib/in-app-wallet-calls.js"
"../in-app/core/eip5972/in-app-wallet-calls.js"
);
return inAppWalletGetCallsStatus({ wallet, client, bundleId });
}
Expand Down
2 changes: 1 addition & 1 deletion packages/thirdweb/src/wallets/eip5792/get-capabilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export async function getCapabilities<const ID extends WalletId = WalletId>({

if (isInAppWallet(wallet)) {
const { inAppWalletGetCapabilities } = await import(
"../in-app/core/lib/in-app-wallet-capabilities.js"
"../in-app/core/eip5972/in-app-wallet-capabilities.js"
);
return inAppWalletGetCapabilities({ wallet });
}
Expand Down
2 changes: 1 addition & 1 deletion packages/thirdweb/src/wallets/eip5792/send-calls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ export async function sendCalls<const ID extends WalletId>(
// These conveniently operate the same
if (isSmartWallet(wallet) || isInAppWallet(wallet)) {
const { inAppWalletSendCalls } = await import(
"../in-app/core/lib/in-app-wallet-calls.js"
"../in-app/core/eip5972/in-app-wallet-calls.js"
);
return inAppWalletSendCalls({ account, calls });
}
Expand Down
Loading

0 comments on commit f55fa4c

Please sign in to comment.