swiftでEthereumウォレット開発


はじめに

おかげさまで、Ethereum上のERC20に準拠したトークンが誰でも簡単に作れるようになった。
ただ、それに対応したiosウォレットの作り方の記事が少ないので、だれかの参考までになればと。
本項ではHD walletを作るが、こっちの方がセキュリティが強いよなどあれば随時コメント頂ければ幸いです

完成したものをTabControllerとか使ってみやすくしたものがこちらです。参考までに。
https://github.com/taiking/TippingERC20

おことわり

web3swiftというライブラリを使って必要最低限でwalletを作成するが、セキュリティを担保しているものではありません。自己責任でお願いします。

インストール

Podfileに以下の行を追加し、pod install

pod 'web3swift', git: 'https://github.com/BANKEX/web3swift.git'

実装前の準備

トークンの作成

長くなるので詳細は書きませんが、truffleというフレームワークを使うと簡単に作れます。
こちらを参考に。
https://github.com/taiking/coin

ネットワークの作成

トークンが作れたら、とりあえずローカルでEthereumのネットワークを作ります。
Ganacheというのがおすすめ。
ソフトを開くだけでネットワークが作られるのでとても簡単。
ちなみに、iosではhttp://127.0.0.1:8545 が開かれる想定で実装しています。

コントラクトのデプロイ

truffleでプロジェクトが作れたらデプロイできたら、abiとcontract addressを控えておく。あとで使うので。

iosの実装

やっと本題、ios側の実装の説明をします。

鍵の作成

mnemonicから鍵を作ります。これは初回起動時だけでいいかと思います。

// 送金の際に必要なパスワード
let password = "hogehoge"

// 鍵の作成と取得
let userDir = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
var keystoreManager = KeystoreManager.managerForPath(userDir + "/keystore", scanForHDwallets: true)
let mnemonic = try! BIP39.generateMnemonics(bitsOfEntropy: 256)!
let keystore = try! BIP32Keystore(mnemonics: mnemonic, password: password, mnemonicsPassword: "", language: .english)!
let keydata = try! JSONEncoder().encode(keystore.keystoreParams)
FileManager.default.createFile(atPath: userDir + "/keystore"+"/key.json", contents: keydata, attributes: nil)
keystoreManager = KeystoreManager.managerForPath(userDir + "/keystore", scanForHDwallets: true)

NSSearchPathForDirectoriesInDomains で鍵情報を保存するディレクトリを指定しています。
.documentDirectory の部分は保存するデータがどういうものかによって変更します。
こちらを参考にしてください。

これで、documentディレクトリに鍵情報が保存されました。

残高の取得

ここではEthの残高とトークンの残高を取得します。


// ローカルネットワークの接続(本番やropstenに繋ぐ場合はここを変更)
let network = Web3.new(URL(string: "http://127.0.0.1:8545")!)!

// 先ほど控えたコントラクトのabiとcontract addressを設定
let contractAbi = "contract abi"
let contractAddress = EthereumAddress("contract address")

// ローカルのウォレットアドレスを取得
let address = keystoreManager!.addresses!.first!

// Ethの残高取得
guard case .success(var ethBalance) = network.eth.getBalance(address: address) else { return }
ethBalance = ethBalance / BigUInt(pow(10, 18).description)!
print(ethBalance)

// トークンの残高取得
let contract = network.contract(contractAbi, at: contractAddress, abiVersion: 2)!
var options = Web3Options.defaultOptions()
options.from = address
guard let balanceResult = contract.method("balanceOf", parameters: [address] as [AnyObject], options: options)?.call(options: nil) else { return }
guard case .success(let result) = balanceResult, var tokenBalance = result["0"] as? BigUInt else { return }
tokenBalance = tokenBalance / BigUInt(pow(10, 18).description)!
print(tokenBalance)

Ethの送金

やっとEthereumの送金を行います。

// Ethの送金
let value = 0.5  // 送金するEthの量
let toAddress = EthereumAddress("to address") // 送金先のアドレス
let coldWalletABI = "[{\"payable\":true,\"type\":\"fallback\"}]"
var options = Web3Options.defaultOptions()
options.value = BigUInt(value * Double(pow(10, 18).description)!)
options.from = address
let intermediate = network.contract(coldWalletABI, at: toAddress, abiVersion: 2)!.method(options: options)!
let sendResult = intermediate.send(password: password)
switch sendResult {
case .success(let r):
    print(r)
case .failure(let err):
    print(err)
}

トークンの送信

最後に、独自トークンの送金を行います。


// トークンの送金
let value = 0.5 // 送金するトークンの量
var options = Web3Options.defaultOptions()
options.from = address
let bigValue = BigUInt(value * Double(pow(10, 18).description)!) as AnyObject
network.addKeystoreManager(keystoreManager) // 秘密鍵をセット
let contract = network.contract(contractAbi, at: contractAddress, abiVersion: 2)!
let parameters: [AnyObject] = [address as AnyObject, bigValue]
let intermediate = contract.method("transfer", parameters: parameters, options: options)
guard let sendTokenResult = intermediate?.send(password: password) else { return } // keystoreを作った時のパスワードが必要
switch sendTokenResult {
case .success(let r):
    print(r)
case .failure(let err):
    print(err)
}

まとめ

ここまでシンプルに書けるとは先人に感謝です。
あとは。ウォレットのインポートとかできるようになりたいなと思う所存です。