ブロックチェーンウォレット開発時のエラー解決備忘録 Cannot read property 'slice' of undefined

# ブロックチェーンウォレット開発時のエラー解決備忘録

## エラー内容
ブロックチェーンウォレットの開発中に以下のようなエラーが発生しました。このエラーは特定の環境下でのみ発生するものです。

エラー例 1
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/633687/51b9055d-9784-83c9-1250-b17892475805.png)

エラー例 2
![Screenshot 2024-06-30 at 12.18.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/633687/4fd28e30-5037-13f4-8747-ff8e664b662b.png)

※ なお、エラー原因の想定がつく方はこの画像を貼ったことに違和感があるかもしれません。はい、そうです。これは依存関係の解決をミスっているだけなので、このエラーは結果として発生したエラーのうちの1つですね。

## 環境
- **Expo**
- **Expo-Router** による Page Router （entrypoint が `expo-router/entry`）
- Expo 上で `crypto` モジュール等、標準では対応していないモジュールを利用するため、置き換えが必要

## エラーの原因
1. **以前の開発手法**:
    - Page Router ではない開発時には `App.ts` に `shim.js` をセットし、`shim.js` に置き換えロジックなどを記載していました。
    
2. **現在の開発手法**:
    - Page Router のチュートリアルに従うと、この `App.ts` は不要となり、`expo-router/entry` に隠蔽されています。
    - そのため、`app/_layout.ts` に `shim.js` を設置していました。

3. **問題点**:
    - 上記の方法では、`shim.js` の読み込み前に他のモジュールの解決が開始されてしまい、不定期にエラーが発生しました。

## 解決策

**隠蔽されている `App.ts` を取り出す**:

`App.ts` を取り出し、その中に `shim.js` を設置することで、モジュールの解決が `shim.js` の読み込み後に開始されるようにします。

```tsx
// App.ts
import 'shim.js';  // 必要なモジュール置き換えロジックを記載

import { ExpoRouterApp } from 'expo-router';
export default function App() {
  return <ExpoRouterApp />;
}
```

具体的なコードで以下の通りに説明していきます。

**package.json**

まず package.json の main を書き換えます

```diff_json
{
  "type": "module",
  "version": "1.0.0",
-  "main": "expo-router/entry",
+  "main": "App.ts",
}
```

次に今回色々ごにょごにょとグローバルモジュール等を置換するための shim.js を作成します。

**shim.js**

```javascript
const bprocess = require('process');

if (typeof __dirname === 'undefined') global.__dirname = '/';
if (typeof __filename === 'undefined') global.__filename = '';
global.process = bprocess;
bprocess.browser = false;
global.Buffer = require('buffer').Buffer;
const isDev = typeof __DEV__ === 'boolean' && __DEV__;
bprocess.env['NODE_ENV'] = isDev ? 'development' : 'production';
```

**App.ts**

次に `expo-router/entry` の代わりに使用する entrypoint である `App.ts` を作成します。このコードは最初の shim import 以外は `expo-router/entry` からコピーしてきたものです。

```typescript
import './shim';
import '@expo/metro-runtime';
import { App } from 'expo-router/build/qualified-entry';
import { renderRootComponent } from 'expo-router/build/renderRootComponent';
renderRootComponent(App);
```

参考迄に、今回 `symbol-sdk` を使用しているのですが、 `metro.config.cjs` のファイル記載しておきます。これは使用するチェーンによって設定が異なります。（ EVM 系のチェーンは Web3Auth.js とか ThirdWeb SDK を使用すれば多分こんな書かなくて良いと思うので、もっと楽だと思います ）

ed25519 の wasm 実装が react native 環境だとちょっと動かす難易度が高いので削除して、pollyfill の ed25519 を使用するようにしています。

**metro.config.cjs**

```javascript
/* eslint-env node */
// Learn more https://docs.expo.io/guides/customizing-metro
const { getDefaultConfig } = require('expo/metro-config');

const defaultConfig = getDefaultConfig(__dirname);

/** @type {import('expo/metro-config').MetroConfig} */
const config = {
  ...defaultConfig,
  resolver: {
    resolveRequest: (context, moduleName, platform) => {
      // skip loading wasm (not supported), an empty module will be loaded in place
      if (moduleName === './ed25519_wasm.js') {
        return { type: 'empty' };
      }

      // rewrite symbol-sdk subpath includes with src/ intermediate directory
      // due to limited exports support in final bundle
      const SYMBOL_SDK_SUBPATH_IMPORT = 'symbol-sdk/';
      if (moduleName.startsWith(SYMBOL_SDK_SUBPATH_IMPORT)) {
        moduleName = `symbol-sdk/src/${moduleName.substring(SYMBOL_SDK_SUBPATH_IMPORT.length)}`;
      }

      // chain to the standard Metro resolver for everything else
      return context.resolveRequest(context, moduleName, platform);
    },
    extraNodeModules: {
      crypto: require.resolve('@symbol-blockchain-community/expo-symbol-crypto'),
      stream: require.resolve('stream-browserify'),
      buffer: require.resolve('buffer'),
      events: require.resolve('events'),
      asserts: require.resolve('assert'),
      url: require.resolve('url'),
      process: require.resolve('process'),
      util: require.resolve('util'),
    },
    sourceExts: [...defaultConfig.resolver.sourceExts, 'svg', 'cjs'],
  },
};

module.exports = config;
```

以上で完了です。もしこれでもなんかエラー変わんない... という時は一旦エミュレーターをリセットして、かつ以下のようなコマンドで依存関係を再インストールしましょう。それでも動かないときは... 別の原因でしょう。

```bash
rm -rf .expo node_modules
watchman watch-del-all
rm -rf $TMPDIR/metro-*
npm install
npx expo start -c
```

これにより、開発中の不定期なエラーを解決することができました。いつか、python のネイティブアプリ開発のエコシステムももう少し大きくなってくれると嬉しいなぁ（遠い目

**symbol-sdk**

https://www.npmjs.com/package/symbol-sdk

**今回のコードを使用しているレポジトリ**
（2024-07-01 時点）

https://github.com/symbol-blockchain-community/symbol-wallet-base