فهرست
اکانت ها در اتریوم یا آدرس کیف پول هستن یا آدرس قراردادهای هوشمند.
آدرسها شکلی شبیه به این دارن: 0x71c7656ec7ab88b098defb751b7401b5f6d8976f
و برای ارسال اتریوم به بقیه کاربران و همینطور ارجاع به یک قرارداد هوشمند روی بلاکچین در زمانی که نیاز به تعامل با باهاش داشته باشیم، مورد استفاده قرار میگیرن. آدرسها منحصر به فرد هستن و از یک کلید خصوصی ایجاد میشن. در بخشهای بعدی بیشتر در مورد جفت کلیدهای عمومی/خصوصی صحبت میکنیم.
برای استفاده از آدرسهای حساب با go-ethereum، اول باید به نوع common.Address توی go-ethereum تبدیلشون کرد.
address := common.HexToAddress("0x71c7656ec7ab88b098defb751b7401b5f6d8976f") fmt.Println(address.Hex()) // 0x71C7656EC7ab88b098defB751B7401B5f6d8976F
این نوع common.Address رو هر جایی که نیاز به پاس دادن آدرس اتریوم به متد های go-ethereum هست میبینیم.
کد کامل
package main import ( "fmt" "github.com/ethereum/go-ethereum/common" ) func main() { address := common.HexToAddress("0x71c7656ec7ab88b098defb751b7401b5f6d8976f") fmt.Println(address.Hex()) // 0x71C7656EC7ab88b098defB751B7401B5f6d8976F fmt.Println(address.Hash().Hex()) // 0x00000000000000000000000071c7656ec7ab88b098defb751b7401b5f6d8976f fmt.Println(address.Bytes()) // [113 199 101 110 199 171 136 176 152 222 251 117 27 116 1 181 246 216 151 111] }
حالا که مبانی پایه ای اکانت و آدرسها رو دیدیم، توی این بخش بالانس اکانت ها رو میگیریم.
خوندن بالانس اکانت سادست و فقط لازمه متد BalanceAt کلاینتمون رو صدا بزنیم و آدرس اکانت مورد نظرمونو بهش بدیم، به صورت انتخابی هم شماره بلاک میگیره که اگه nill قرار بدیم آخرین بالانس اکانت رو برمیگردونه.
account := common.HexToAddress("0x71c7656ec7ab88b098defb751b7401b5f6d8976f") balance, err := client.BalanceAt(context.Background(), account, nil) if err != nil { log.Fatal(err) } fmt.Println(balance) // 25893180161173005034
ولی اگه شماره بلاک رو مشخص کنیم بهمون گرفتن بالانس اکانت توی زمان اون بلاک رو میده. شماره بلاک هم باید از جنس big.Int باشه.
blockNumber := big.NewInt(5532993) balance, err := client.BalanceAt(context.Background(), account, blockNumber) if err != nil { log.Fatal(err) } fmt.Println(balance) // 25729324269165216042
اعداد توی اتریوم با کوچکترین واحد ممکن محاسبه میشن و اون واحد wei هست، برای تبدیل مقداری که اینجا گرفتیم به ETH باید محاسبه wei / 10^18 رو انجام بدیم. از اونجایی که با اعداد Big سروکار داریم باید از پکیج های math و math/big استفاده کنیم. روش انجام این محاسبه به شکل زیره.
fbalance := new(big.Float) fbalance.SetString(balance.String()) ethValue := new(big.Float).Quo(fbalance, big.NewFloat(math.Pow10(18))) fmt.Println(ethValue) // 25.729324269165216041
بالانس در انتظار
بعضی اوقات میخوایم بدونیم موجودی pending اکانت چقدره، مثلا زمانی که تازه یه تراکنش ثبت کردیم یا منتظر تایید یه تراکنش به اکانتیم. برای همچین مواقعی کلاینت یه متد مشابه BalanceAt به نام PendingBalanceAt داره که آدرس اکانت رو توی ورودیش میگیره.
pendingBalance, err := client.PendingBalanceAt(context.Background(), account) fmt.Println(pendingBalance) // 25729324269165216042
کد کامل
package main import ( "context" "fmt" "log" "math" "math/big" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethclient" ) func main() { client, err := ethclient.Dial("https://cloudflare-eth.com") if err != nil { log.Fatal(err) } account := common.HexToAddress("0x71c7656ec7ab88b098defb751b7401b5f6d8976f") balance, err := client.BalanceAt(context.Background(), account, nil) if err != nil { log.Fatal(err) } fmt.Println(balance) // 25893180161173005034 blockNumber := big.NewInt(5532993) balanceAt, err := client.BalanceAt(context.Background(), account, blockNumber) if err != nil { log.Fatal(err) } fmt.Println(balanceAt) // 25729324269165216042 fbalance := new(big.Float) fbalance.SetString(balanceAt.String()) ethValue := new(big.Float).Quo(fbalance, big.NewFloat(math.Pow10(18))) fmt.Println(ethValue) // 25.729324269165216041 pendingBalance, err := client.PendingBalanceAt(context.Background(), account) fmt.Println(pendingBalance) // 25729324269165216042 }
توی این قسمت اعتبارسنجی یک آدرس رو توضیح میدیم و اینکه چطور میشه تشخیص داد آدرس متعلق به یک قرارداد هوشمند هست یا نه.
اعتبارسنجی آدرس
میتونیم از یه Regex ساده برای اعتبارسنجی آدرسهای اتریوم استفاده کنیم.
re := regexp.MustCompile("^0x[0-9a-fA-F]{40}$") fmt.Printf("is valid: %v\n"), re.MatchString("0x323b5d4c32345ced77393b3530b1eed0f346429d")) // is valid: true fmt.Printf("is valid: %v\n", re.MatchString("0xZYXb5d4c32345ced77393b3530b1eed0f346429d")) // is valid: false
آدرس کیف پول یا قرارداد هوشمند
اگه بایتکدی برای آدرس ذخیره شده باشه میتونیم تشخیص بدیم که آدرس متعلق به قرارداد هوشمنده. توی قطعه کد زیر بایتکد قرارداد هوشمند یک توکن رو بازیابی میکنیم و طولش رو برای تایید اینکه قرارداد هوشمنده بررسی میکنیم.
// 0x Protocol Token (ZRX) smart contract address address := common.HexToAddress("0xe41d2489571d322189246dafa5ebde1f4699f498") bytecode, err := client.CodeAt(context.Background(), address, nil) // nil is latest block if err != nil { log.Fatal(err) } isContract := len(bytecode) > 0 fmt.Printf("is contract: %v\n", isContract) // is contract: true
زمانی که بایتکدی وجود نداره میدونیم که آدرس متعلق به قرارداد هوشمندی نیست و برای یه اکانت استاندارد اتریومه.
// a random user account address address := common.HexToAddress("0x8e215d06ea7ec1fdb4fc5fd21768f4b34ee92ef4") bytecode, err := client.CodeAt(context.Background(), address, nil) // nil is latest block if err != nil { log.Fatal(err) } isContract = len(bytecode) > 0 fmt.Printf("is contract: %v\n", isContract) // is contract: false
کد کامل
package main import ( "context" "fmt" "log" "regexp" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethclient" ) func main() { re := regexp.MustCompile("^0x[0-9a-fA-F]{40}$") fmt.Printf("is valid: %v\n", re.MatchString("0x323b5d4c32345ced77393b3530b1eed0f346429d")) // is valid: true fmt.Printf("is valid: %v\n", re.MatchString("0xZYXb5d4c32345ced77393b3530b1eed0f346429d")) // is valid: false client, err := ethclient.Dial("https://cloudflare-eth.com") if err != nil { log.Fatal(err) } // 0x Protocol Token (ZRX) smart contract address address := common.HexToAddress("0xe41d2489571d322189246dafa5ebde1f4699f498") bytecode, err := client.CodeAt(context.Background(), address, nil) // nil is latest block if err != nil { log.Fatal(err) } isContract := len(bytecode) > 0 fmt.Printf("is contract: %v\n", isContract) // is contract: true // a random user account address address = common.HexToAddress("0x8e215d06ea7ec1fdb4fc5fd21768f4b34ee92ef4") bytecode, err = client.CodeAt(context.Background(), address, nil) // nil is latest block if err != nil { log.Fatal(err) } isContract = len(bytecode) > 0 fmt.Printf("is contract: %v\n", isContract) // is contract: false }
توی پست بعدی روش ساخت کیف پول جدید و ذخیره سازی امن کلید خصوصی رو بررسی میکنیم.