mirror of
https://github.com/galacticship/terra.git
synced 2024-11-13 19:57:51 +00:00
265 lines
6.5 KiB
Go
265 lines
6.5 KiB
Go
|
package terra
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"encoding/json"
|
||
|
"fmt"
|
||
|
"net/url"
|
||
|
|
||
|
"github.com/galacticship/terra/cosmos"
|
||
|
"github.com/galacticship/terra/terra"
|
||
|
"github.com/pkg/errors"
|
||
|
"github.com/shopspring/decimal"
|
||
|
)
|
||
|
|
||
|
type tokenInfo struct {
|
||
|
Name string `json:"name"`
|
||
|
Symbol string `json:"symbol"`
|
||
|
Decimals uint8 `json:"decimals"`
|
||
|
TotalSupply decimal.Decimal `json:"total_supply"`
|
||
|
}
|
||
|
|
||
|
type Token interface {
|
||
|
Id() string
|
||
|
Address() cosmos.AccAddress
|
||
|
Symbol() string
|
||
|
Decimals() uint8
|
||
|
Balance(context.Context, *Querier, cosmos.AccAddress) (decimal.Decimal, error)
|
||
|
|
||
|
IsNative() bool
|
||
|
|
||
|
ValueFromTerra(value decimal.Decimal) decimal.Decimal
|
||
|
ValueToTerra(value decimal.Decimal) decimal.Decimal
|
||
|
|
||
|
NewMsgSendExecute(sender cosmos.AccAddress, contract *Contract, amount decimal.Decimal, execMsg interface{}) (cosmos.Msg, error)
|
||
|
|
||
|
Equals(Token) bool
|
||
|
|
||
|
String() string
|
||
|
}
|
||
|
|
||
|
type Cw20Token struct {
|
||
|
address cosmos.AccAddress
|
||
|
symbol string
|
||
|
decimals uint8
|
||
|
}
|
||
|
|
||
|
func NewCw20Token(contractAddress string, symbol string, decimals uint8) (Cw20Token, error) {
|
||
|
accaddress, err := cosmos.AccAddressFromBech32(contractAddress)
|
||
|
if err != nil {
|
||
|
return Cw20Token{}, errors.Wrap(err, "validating token contract address")
|
||
|
}
|
||
|
return Cw20Token{
|
||
|
address: accaddress,
|
||
|
symbol: symbol,
|
||
|
decimals: decimals,
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
func Cw20TokenFromAddress(ctx context.Context, querier *Querier, contractAddress string) (Cw20Token, error) {
|
||
|
if t, ok := Cw20TokenMap[contractAddress]; ok {
|
||
|
return t, nil
|
||
|
}
|
||
|
accaddress, err := cosmos.AccAddressFromBech32(contractAddress)
|
||
|
if err != nil {
|
||
|
return Cw20Token{}, errors.Wrap(err, "validating token contract address")
|
||
|
}
|
||
|
t := Cw20Token{
|
||
|
address: accaddress,
|
||
|
}
|
||
|
ti, err := t.getTokenInfo(ctx, querier)
|
||
|
if err != nil {
|
||
|
return Cw20Token{}, errors.Wrap(err, "getting token info")
|
||
|
}
|
||
|
t.symbol = ti.Symbol
|
||
|
t.decimals = ti.Decimals
|
||
|
|
||
|
return t, nil
|
||
|
}
|
||
|
|
||
|
func (t Cw20Token) getTokenInfo(ctx context.Context, querier *Querier) (tokenInfo, error) {
|
||
|
var ti tokenInfo
|
||
|
query := struct {
|
||
|
TokenInfo struct{} `json:"token_info"`
|
||
|
}{}
|
||
|
contract, err := NewContract(querier, t.address.String())
|
||
|
if err != nil {
|
||
|
return tokenInfo{}, errors.Wrap(err, "creating contract object")
|
||
|
}
|
||
|
err = contract.QueryStore(ctx, query, &ti)
|
||
|
if err != nil {
|
||
|
return tokenInfo{}, errors.Wrap(err, "calling token_info contract method")
|
||
|
}
|
||
|
return ti, nil
|
||
|
}
|
||
|
|
||
|
func (t Cw20Token) Id() string {
|
||
|
return t.Address().String()
|
||
|
}
|
||
|
|
||
|
func (t Cw20Token) Decimals() uint8 {
|
||
|
return t.decimals
|
||
|
}
|
||
|
|
||
|
func (t Cw20Token) DecimalsAsInt32() int32 {
|
||
|
return int32(t.decimals)
|
||
|
}
|
||
|
|
||
|
func (t Cw20Token) Symbol() string {
|
||
|
return t.symbol
|
||
|
}
|
||
|
|
||
|
func (t Cw20Token) Address() cosmos.AccAddress {
|
||
|
return t.address
|
||
|
}
|
||
|
|
||
|
func (t Cw20Token) ValueFromTerra(value decimal.Decimal) decimal.Decimal {
|
||
|
return value.Shift(-t.DecimalsAsInt32())
|
||
|
}
|
||
|
|
||
|
func (t Cw20Token) ValueToTerra(value decimal.Decimal) decimal.Decimal {
|
||
|
return value.Shift(t.DecimalsAsInt32())
|
||
|
}
|
||
|
|
||
|
func (t Cw20Token) Balance(ctx context.Context, querier *Querier, address cosmos.AccAddress) (decimal.Decimal, error) {
|
||
|
type query struct {
|
||
|
Balance struct {
|
||
|
Address string `json:"address"`
|
||
|
} `json:"balance"`
|
||
|
}
|
||
|
type response struct {
|
||
|
Balance decimal.Decimal `json:"balance"`
|
||
|
}
|
||
|
var q query
|
||
|
q.Balance.Address = address.String()
|
||
|
var r response
|
||
|
contract, err := NewContract(querier, t.address.String())
|
||
|
if err != nil {
|
||
|
return decimal.Zero, errors.Wrap(err, "creating contract object")
|
||
|
}
|
||
|
err = contract.QueryStore(ctx, q, &r)
|
||
|
if err != nil {
|
||
|
return decimal.Zero, errors.Wrap(err, "querying contract store")
|
||
|
}
|
||
|
return t.ValueFromTerra(r.Balance), nil
|
||
|
}
|
||
|
|
||
|
func (t Cw20Token) IsNative() bool {
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
func (t Cw20Token) Equals(token Token) bool {
|
||
|
if !token.IsNative() && t.symbol == token.Symbol() {
|
||
|
return true
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
func (t Cw20Token) String() string {
|
||
|
return t.symbol
|
||
|
}
|
||
|
|
||
|
func (t Cw20Token) NewMsgSendExecute(sender cosmos.AccAddress, contract *Contract, amount decimal.Decimal, execMsg interface{}) (cosmos.Msg, error) {
|
||
|
jsonexecmsg, err := json.Marshal(execMsg)
|
||
|
if err != nil {
|
||
|
return nil, errors.Wrap(err, "marshalling execMsg")
|
||
|
}
|
||
|
type query struct {
|
||
|
Send struct {
|
||
|
Contract string `json:"contract"`
|
||
|
Amount decimal.Decimal `json:"amount"`
|
||
|
Message interface{} `json:"msg"`
|
||
|
} `json:"send"`
|
||
|
}
|
||
|
var q query
|
||
|
q.Send.Contract = contract.Address().String()
|
||
|
q.Send.Amount = t.ValueToTerra(amount)
|
||
|
q.Send.Message = jsonexecmsg
|
||
|
return terra.NewMsgExecuteContract(sender, t.Address(), q, nil)
|
||
|
}
|
||
|
|
||
|
type NativeToken struct {
|
||
|
symbol string
|
||
|
denom string
|
||
|
}
|
||
|
|
||
|
func NewNativeToken(symbol string, denom string) NativeToken {
|
||
|
return NativeToken{
|
||
|
denom: denom,
|
||
|
symbol: symbol,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func NativeTokenFromDenom(denom string) NativeToken {
|
||
|
if t, ok := NativeTokenMap[denom]; ok {
|
||
|
return t
|
||
|
}
|
||
|
return NewNativeToken("", denom)
|
||
|
}
|
||
|
|
||
|
func (n NativeToken) Id() string {
|
||
|
return n.denom
|
||
|
}
|
||
|
|
||
|
func (n NativeToken) Address() cosmos.AccAddress {
|
||
|
return cosmos.AccAddress{}
|
||
|
}
|
||
|
|
||
|
func (n NativeToken) Symbol() string {
|
||
|
return n.symbol
|
||
|
}
|
||
|
func (n NativeToken) Denom() string {
|
||
|
return n.denom
|
||
|
}
|
||
|
|
||
|
func (n NativeToken) Decimals() uint8 {
|
||
|
return 6
|
||
|
}
|
||
|
|
||
|
func (n NativeToken) Balance(ctx context.Context, querier *Querier, address cosmos.AccAddress) (decimal.Decimal, error) {
|
||
|
var response struct {
|
||
|
Balance struct {
|
||
|
Denom string `json:"denom"`
|
||
|
Amount decimal.Decimal `json:"amount"`
|
||
|
} `json:"balance"`
|
||
|
}
|
||
|
params := url.Values{}
|
||
|
params.Set("denom", n.Denom())
|
||
|
err := querier.GET(ctx, fmt.Sprintf("cosmos/bank/v1beta1/balances/%s/by_denom", address.String()), params, &response)
|
||
|
if err != nil {
|
||
|
return decimal.Zero, errors.Wrap(err, "executing get request")
|
||
|
}
|
||
|
return n.ValueFromTerra(response.Balance.Amount), nil
|
||
|
}
|
||
|
|
||
|
func (n NativeToken) DecimalsAsInt32() int32 {
|
||
|
return int32(n.Decimals())
|
||
|
}
|
||
|
|
||
|
func (n NativeToken) ValueFromTerra(value decimal.Decimal) decimal.Decimal {
|
||
|
return value.Shift(-n.DecimalsAsInt32())
|
||
|
}
|
||
|
|
||
|
func (n NativeToken) ValueToTerra(value decimal.Decimal) decimal.Decimal {
|
||
|
return value.Shift(n.DecimalsAsInt32())
|
||
|
}
|
||
|
|
||
|
func (n NativeToken) IsNative() bool {
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
func (n NativeToken) Equals(token Token) bool {
|
||
|
if token.IsNative() && n.symbol == token.Symbol() {
|
||
|
return true
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
func (n NativeToken) String() string {
|
||
|
return n.symbol
|
||
|
}
|
||
|
|
||
|
func (n NativeToken) NewMsgSendExecute(sender cosmos.AccAddress, contract *Contract, amount decimal.Decimal, execMsg interface{}) (cosmos.Msg, error) {
|
||
|
return terra.NewMsgExecuteContract(sender, contract.contractAddress, execMsg, cosmos.NewCoins(cosmos.NewInt64Coin(n.Id(), n.ValueToTerra(amount).IntPart())))
|
||
|
}
|