CosmosOsmosisアビトラ
created at
CosmosアビトラBotの作り方 その5【コード付き】
前回までで一旦パスの選定を行ったので、実際にCosmosSDKを使ってトランザクションを送る準備に入ります。(cosmosSDKはバージョン0.45系を使用しています。)
ウォレットの準備
まずは、トランザクションの署名に必要なウォレットを定義し、アドレスや公開鍵、秘密鍵を管理できるようにします。
Cosmosブロックチェーンではシークレットから鍵を生成するためにBIP39 (Bitcoin Improvement Proposal 39)という規画を採用しています。BIP39でサポートされている階層的キー生成という仕組みを用いれば、単一のシークレットを用いて複数のブロックチェーン上で使用できるキーを生成することができます。HDキー導出の手順はhttps://github.com/confio/cosmos-hd-key-derivation-specを参考にしました。
あるキーフレーズが与えられた時に公開鍵と秘密鍵を生成する具体的な手順は以下のようになります。
-
bip39.NewSeed関数を使用して、キーフレーズからシードを生成。
-
hd.ComputeMastersFromSeed関数を使用して、生成されたシードからマスター秘密鍵とチェーンコードを計算。
-
BIP44で規定されているHDパスから秘密鍵を導出。
-
secp256k1.PrivKeyを使用して秘密鍵をラップし、その秘密キーから公開鍵を生成。
-
公開鍵をSHA-256とRIPEMD-160を用いてハッシュ化。その結果を基に、Bech32エンコードを使用してアドレスを生成。
具体的なコードは以下のようになります。utilsというディレクトリを作り、ここでウォレットの管理を行います。この後はmainからGet_keys_from_seed関数を使って構造体Walletを取得する流れになります。
package utils
import (
"crypto/sha256"
"github.com/cosmos/cosmos-sdk/crypto/hd"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
"github.com/cosmos/go-bip39"
"github.com/decred/dcrd/bech32"
ripemd160 "golang.org/x/crypto/ripemd160"
"strconv"
)
type Wallet struct {
Public_key cryptotypes.PubKey
Adress string
Private_key cryptotypes.PrivKey
Ripemd []byte
Account_Sequence uint64
}
func Get_keys_from_seed(phrase string, initial_account_sequence string) Wallet {
//パスフレーズは空文字列として与える。
seed := bip39.NewSeed(phrase, "")
master, ch := hd.ComputeMastersFromSeed(seed)
// CosmosHubにおけるBIP44型のHDパス
path := "m/44'/118'/0'/0/0"
priv, _ := hd.DerivePrivateKeyForPath(master, ch, path)
var privKey = secp256k1.PrivKey{Key: priv}
pubKey := privKey.PubKey()
sha := sha256.Sum256(pubKey.Bytes())
hasherRIPEMD160 := ripemd160.New()
hasherRIPEMD160.Write(sha[:])
ripemd := hasherRIPEMD160.Sum(nil)
// base32にコンバートするのが肝:
conv, _ := bech32.ConvertBits(ripemd, 8, 5, true)
encoded, _ := bech32.Encode("osmo", conv)
public_key := &secp256k1.PubKey{Key: pubKey.Bytes()}
private_key := &privKey
//アカウントシーケンス(トランザクションを送った回数)はトランザクション生成で使用。
account_sequence, _ := strconv.ParseUint(initial_account_sequence, 10, 64)
return Wallet{Public_key: public_key, Private_key: private_key, Adress: encoded, Ripemd: ripemd, Account_Sequence: account_sequence}
}
account_sequenceとはコメントの通り、そのアドレスからトランザクションを送った回数を示すもので、初期状態は0、トランザクションを送るたびに+1されていきます。トランザクションの順序を強制させることでリプレイ攻撃を防ぐ目的があります。イーサリアムで言うところのnonceのようなものと思って構わないです。
Osmosis SDKを使う。
さてOsmosisのAMMプールでのスワップを実装するにあたり、osmosis SDKを一部使っていきます。使っていくのですが、いろいろな互換性の問題が多く、前準備としてgo modファイル内で以下のような呪文を唱える必要があります。
replace ( github.com/CosmWasm/wasmd => github.com/osmosis-labs/wasmd v0.29.2-osmo-v13 github.com/confio/ics23/go => github.com/cosmos/cosmos-sdk/ics23/go v0.8.0 github.com/cosmos/cosmos-sdk => github.com/osmosis-labs/cosmos-sdk v0.45.1-0.20221118211718-545aed73e94e github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 google.golang.org/grpc => google.golang.org/grpc v1.33.2 )
importステートメントには先ほど作成したutilsとosmosis/v13からgamm/typesを拾ってきます。ちなみにosmosisの最新バージョンは20らしいです。対応させるのが面倒なので私は13を使います。
import ( bf "cosmos-arb/algorithms" "cosmos-arb/utils" //ここからウォレットをインポート "encoding/json" "fmt" gamm "github.com/osmosis-labs/osmosis/v13/x/gamm/types" //ここはosmosis-labから拝借 "io/ioutil" "math" "net/http" "strconv" )
前回の記事はアービトラージpathを求めるところまで紹介しました。このpathをトランザクションに落とし込む準備をします。osmosisモジュール内のgamm.SwapAmountInRouteという構造体を見てみると、プールの番号と放出するトークンのDenomが要求されています。そこで、まず構造体Poolに放出するトークンのDenomを返すメソッドを付け加えます。
func (p *Pool) GetOppositeSide(tokenInDenom string) string {
if tokenInDenom == p.Token1.Denom {
return p.Token2.Denom
} else {
return p.Token1.Denom
}
}
参入トークンと放出トークンの組み合わせからPoolの番号(pool_number)を取得し、これらをgamm.SwapAmountInRouteに渡します。ここは前回のコードのメイン関数内に付け加えてください。
var routes []gamm.SwapAmountInRoute var pool_number int var tokenOut string = "uosmo" for i := 1; i < len(path); i++ { pool_number = poolMap[[2]uint{path[i-1], path[i]}] tokenOut = pool_information[pool_number].GetOppositeSide(tokenOut) routes = append(routes, gamm.SwapAmountInRoute{PoolId: uint64(pool_number), TokenOutDenom: tokenOut}) }
こうしてできたroutesをプリントしてみると以下のようになるかと思います。
Arbitrage Path: [54 162 81 54] [{3 ibc/1480B8FD20AD5FCAE81EA87584D269547DD4D436843C1D20F15E00EB64743EF4} {4 ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2} {1 uosmo}]
この例では番号が3,4,1のプールを使って、uosmo→ibc/1480B8FD20AD5FCAE81EA87584D269547DD4D436843C1D20F15E00EB64743EF4→ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2→uosmoのような閉路を検出することに成功しています。
次回はこのroutesをメッセージというものに包み込み、実際にトランザクションの生成&署名を行なっていきます。