evm/token.go

141 lines
3.9 KiB
Go
Raw Normal View History

2021-11-08 18:20:31 +00:00
package evm
import (
"context"
"math/big"
"git.lehouerou.net/laurent/evm/contracts"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/pkg/errors"
"github.com/shopspring/decimal"
)
type Token interface {
2021-11-09 05:07:42 +00:00
Client() Client
2021-11-08 18:20:31 +00:00
Address() common.Address
Name() string
Symbol() string
Decimals() uint8
TotalSupply() (decimal.Decimal, error)
BalanceOf(common.Address) (decimal.Decimal, error)
Balance() (decimal.Decimal, error)
Allowance(owner common.Address, spender common.Address) (decimal.Decimal, error)
2021-11-09 04:11:05 +00:00
Approve(ctx context.Context, spender common.Address, value decimal.Decimal, opts ...ExecutionOption) (Transaction, error)
Transfer(ctx context.Context, to common.Address, value decimal.Decimal, opts ...ExecutionOption) (Transaction, error)
2021-11-08 18:20:31 +00:00
ValueToBigInt(value decimal.Decimal) *big.Int
ValueFromBigInt(value *big.Int) decimal.Decimal
}
type baseToken struct {
client Client
contract *contracts.Token
address common.Address
name string
symbol string
decimals uint8
}
func NewToken(client Client, address common.Address) (Token, error) {
contract, err := contracts.NewToken(address, client)
if err != nil {
return nil, errors.WithStack(err)
}
name, err := contract.Name(&bind.CallOpts{})
if err != nil {
return nil, errors.WithStack(err)
}
symbol, err := contract.Symbol(&bind.CallOpts{})
if err != nil {
return nil, errors.WithStack(err)
}
decimals, err := contract.Decimals(&bind.CallOpts{})
if err != nil {
return nil, errors.WithStack(err)
}
return &baseToken{
address: address,
name: name,
symbol: symbol,
decimals: decimals,
client: client,
contract: contract,
}, nil
}
2021-11-09 05:07:42 +00:00
func (t *baseToken) Client() Client {
return t.client
}
2021-11-08 18:20:31 +00:00
func (t *baseToken) TotalSupply() (decimal.Decimal, error) {
s, err := t.contract.TotalSupply(&bind.CallOpts{})
if err != nil {
return decimal.Zero, errors.Wrap(err, "calling contract")
}
return t.ValueFromBigInt(s), nil
}
func (t *baseToken) BalanceOf(address common.Address) (decimal.Decimal, error) {
b, err := t.contract.BalanceOf(&bind.CallOpts{}, address)
if err != nil {
return decimal.Zero, errors.Wrap(err, "getting balance from contract")
}
return t.ValueFromBigInt(b), nil
}
func (t *baseToken) Allowance(owner common.Address, spender common.Address) (decimal.Decimal, error) {
a, err := t.contract.Allowance(&bind.CallOpts{}, owner, spender)
if err != nil {
return decimal.Zero, errors.Wrap(err, "calling contract")
}
return t.ValueFromBigInt(a), nil
}
2021-11-09 04:11:05 +00:00
func (t *baseToken) Approve(ctx context.Context, spender common.Address, value decimal.Decimal, opts ...ExecutionOption) (Transaction, error) {
2021-11-08 18:20:31 +00:00
return t.client.Execute(ctx, func(ctx context.Context, options *TransactOpts) (*types.Transaction, error) {
return t.contract.Approve(options.TransactOpts, spender, t.ValueToBigInt(value))
2021-11-09 04:11:05 +00:00
}, opts...)
2021-11-08 18:20:31 +00:00
}
2021-11-09 04:11:05 +00:00
func (t *baseToken) Transfer(ctx context.Context, to common.Address, value decimal.Decimal, opts ...ExecutionOption) (Transaction, error) {
2021-11-08 18:20:31 +00:00
return t.client.Execute(ctx, func(ctx context.Context, options *TransactOpts) (*types.Transaction, error) {
return t.contract.Transfer(options.TransactOpts, to, t.ValueToBigInt(value))
2021-11-09 04:11:05 +00:00
}, opts...)
2021-11-08 18:20:31 +00:00
}
func (t *baseToken) Address() common.Address {
return t.address
}
func (t *baseToken) Name() string {
return t.name
}
func (t *baseToken) Symbol() string {
return t.symbol
}
func (t *baseToken) Decimals() uint8 {
return t.decimals
}
func (t *baseToken) DecimalsAsInt32() int32 {
return int32(t.decimals)
}
func (t *baseToken) ValueFromBigInt(val *big.Int) decimal.Decimal {
return decimal.NewFromBigInt(val, -t.DecimalsAsInt32())
}
func (t *baseToken) ValueToBigInt(val decimal.Decimal) *big.Int {
return val.Shift(t.DecimalsAsInt32()).BigInt()
}
func (t *baseToken) Balance() (decimal.Decimal, error) {
return t.BalanceOf(t.client.PublicAddress())
}