Skip to content

Commit

Permalink
dex,client/asset: add token primitives
Browse files Browse the repository at this point in the history
dex:
Token and Gases are used to specify token info in higher-level
packages, where lgpl builds are not suitable.
Add a test token ID.

client/asset:
The Token type embeds dex.Token and adds a WalletDefinition. This
type is used to register tokens with the package-level RegisterToken
method.

client/asset/eth:
The test token is registered with it's WalletDefinition. The
definition has 1 ConfigOption, limitAllowance, that allows the user
to keep a limited allowance for the swap contract, but with extra
transactions = more fees.
  • Loading branch information
buck54321 authored Jan 9, 2022
1 parent e05191a commit eeddb7e
Show file tree
Hide file tree
Showing 9 changed files with 825 additions and 612 deletions.
56 changes: 55 additions & 1 deletion client/asset/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
var (
driversMtx sync.RWMutex
drivers = make(map[uint32]Driver)
tokens = make(map[uint32]*Token)
)

// CreateWalletParams are the parameters for internal wallet creation. The
Expand Down Expand Up @@ -70,6 +71,23 @@ func Register(assetID uint32, driver Driver) {
drivers[assetID] = driver
}

// RegisterToken should be called to register tokens.
func RegisterToken(tokenID uint32, token *dex.Token, walletDef *WalletDefinition) {
driversMtx.Lock()
defer driversMtx.Unlock()
if _, exists := tokens[tokenID]; exists {
panic(fmt.Sprintf("token %d already exists", tokenID))
}
_, exists := drivers[token.ParentID]
if !exists {
panic(fmt.Sprintf("token %d's parent asset %d isn't registered", tokenID, token.ParentID))
}
tokens[tokenID] = &Token{
Token: token,
Definition: walletDef,
}
}

// WalletExists will be true if the specified wallet exists.
func WalletExists(assetID uint32, walletType, dataDir string, settings map[string]string, net dex.Network) (exists bool, err error) {
return exists, withDriver(assetID, func(drv Driver) error {
Expand Down Expand Up @@ -115,6 +133,7 @@ type RegisteredAsset struct {
ID uint32
Symbol string
Info *WalletInfo
Tokens map[uint32]*Token
}

// Assets returns a list of information about supported assets.
Expand All @@ -129,10 +148,30 @@ func Assets() map[uint32]RegisteredAsset {
Info: driver.Info(),
}
}
for tokenID, token := range tokens {
parent, found := assets[token.ParentID]
if !found {
// should be impossible.
fmt.Println("parentless token", tokenID, token.Name)
continue
}
if parent.Tokens == nil {
parent.Tokens = make(map[uint32]*Token, 1)
}
parent.Tokens[tokenID] = token
}
return assets
}

// Info returns the WalletInfo for the specified asset, if supported.
// Token returns *Token for a registered token, or nil if the token is unknown.
func TokenInfo(assetID uint32) *Token {
driversMtx.RLock()
defer driversMtx.RUnlock()
return tokens[assetID]
}

// Info returns the WalletInfo for the specified asset, if supported. Info only
// returns WalletInfo for base chain assets, not tokens.
func Info(assetID uint32) (*WalletInfo, error) {
driversMtx.RLock()
drv, ok := drivers[assetID]
Expand All @@ -142,3 +181,18 @@ func Info(assetID uint32) (*WalletInfo, error) {
}
return drv.Info(), nil
}

// UnitInfo returns the dex.UnitInfo for the asset or token.
func UnitInfo(assetID uint32) (dex.UnitInfo, error) {
driversMtx.RLock()
defer driversMtx.RUnlock()
drv, ok := drivers[assetID]
if ok {
return drv.Info().UnitInfo, nil
}
token, ok := tokens[assetID]
if ok {
return token.UnitInfo, nil
}
return dex.UnitInfo{}, fmt.Errorf("asset: unsupported asset %d", assetID)
}
15 changes: 15 additions & 0 deletions client/asset/eth/eth.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,23 @@ import (
"github.com/ethereum/go-ethereum/crypto"
)

var (
testTokenID, _ = dex.BipSymbolID("dextt.eth")
)

func registerToken(tokenID uint32, desc string) {
token := dexeth.Tokens[tokenID]
asset.RegisterToken(tokenID, token, &asset.WalletDefinition{
Type: "token",
Description: desc,
})
}

func init() {
asset.Register(BipID, &Driver{})

// Test token
registerToken(testTokenID, "A token wallet for the DEX test token. Used for testing DEX software.")
}

const (
Expand Down
32 changes: 16 additions & 16 deletions client/asset/eth/eth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -891,9 +891,9 @@ func TestPreSwap(t *testing.T) {

wantLots: 1,
wantValue: ethToGwei(10),
wantMaxFees: 100 * gases.InitGas,
wantBestCase: 90 * gases.InitGas,
wantWorstCase: 90 * gases.InitGas,
wantMaxFees: 100 * gases.Swap,
wantBestCase: 90 * gases.Swap,
wantWorstCase: 90 * gases.Swap,
},
{
name: "more lots than max lots",
Expand All @@ -915,9 +915,9 @@ func TestPreSwap(t *testing.T) {

wantLots: 4,
wantValue: ethToGwei(40),
wantMaxFees: 4 * 100 * gases.InitGas,
wantBestCase: 90 * (gases.InitGas + 3*gases.AdditionalInitGas),
wantWorstCase: 4 * 90 * gases.InitGas,
wantMaxFees: 4 * 100 * gases.Swap,
wantBestCase: 90 * gases.SwapN(4),
wantWorstCase: 4 * 90 * gases.Swap,
},
{
name: "balanceError",
Expand All @@ -936,7 +936,7 @@ func TestPreSwap(t *testing.T) {
ID: 60,
Symbol: "ETH",
MaxFeeRate: 100,
SwapSize: gases.InitGas,
SwapSize: gases.Swap,
SwapSizeBase: 0,
SwapConf: 1,
}
Expand Down Expand Up @@ -1535,10 +1535,10 @@ func TestMaxOrder(t *testing.T) {
maxFeeRate: 100,
wantLots: 1,
wantValue: ethToGwei(10),
wantMaxFees: 100 * gases.InitGas,
wantBestCase: 90 * gases.InitGas,
wantWorstCase: 90 * gases.InitGas,
wantLocked: ethToGwei(10) + (100 * gases.InitGas),
wantMaxFees: 100 * gases.Swap,
wantBestCase: 90 * gases.Swap,
wantWorstCase: 90 * gases.Swap,
wantLocked: ethToGwei(10) + (100 * gases.Swap),
},
{
name: "multiple lots",
Expand All @@ -1548,10 +1548,10 @@ func TestMaxOrder(t *testing.T) {
maxFeeRate: 100,
wantLots: 5,
wantValue: ethToGwei(50),
wantMaxFees: 5 * 100 * gases.InitGas,
wantBestCase: 90 * (gases.InitGas + 4*gases.AdditionalInitGas),
wantWorstCase: 5 * 90 * gases.InitGas,
wantLocked: ethToGwei(50) + (5 * 100 * gases.InitGas),
wantMaxFees: 5 * 100 * gases.Swap,
wantBestCase: 90 * gases.SwapN(5),
wantWorstCase: 5 * 90 * gases.Swap,
wantLocked: ethToGwei(50) + (5 * 100 * gases.Swap),
},
{
name: "balanceError",
Expand All @@ -1568,7 +1568,7 @@ func TestMaxOrder(t *testing.T) {
ID: 60,
Symbol: "ETH",
MaxFeeRate: 100,
SwapSize: gases.InitGas,
SwapSize: gases.Swap,
SwapSizeBase: 0,
SwapConf: 1,
}
Expand Down
8 changes: 4 additions & 4 deletions client/asset/eth/nodeclient_harness_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -558,10 +558,10 @@ func TestInitiateGas(t *testing.T) {
var expectedGas uint64
var actualGas uint64
if i == 1 {
expectedGas = gases.InitGas
expectedGas = gases.Swap
actualGas = gas
} else {
expectedGas = gases.AdditionalInitGas
expectedGas = gases.SwapAdd
actualGas = gas - previousGas
}
if actualGas > expectedGas || actualGas < expectedGas/100*95 {
Expand Down Expand Up @@ -814,10 +814,10 @@ func TestRedeemGas(t *testing.T) {
var expectedGas uint64
var actualGas uint64
if i == 0 {
expectedGas = gases.RedeemGas
expectedGas = gases.Redeem
actualGas = gas
} else {
expectedGas = gases.AdditionalRedeemGas
expectedGas = gases.RedeemAdd
actualGas = gas - previous
}
if actualGas > expectedGas || actualGas < (expectedGas/100*95) {
Expand Down
17 changes: 17 additions & 0 deletions client/asset/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ type WalletDefinition struct {
ConfigOpts []*ConfigOption `json:"configopts"`
}

// Token combines the generic dex.Token with a WalletDefinition.
type Token struct {
*dex.Token
Definition *WalletDefinition `json:"definition"`
}

// WalletInfo is auxiliary information about an ExchangeWallet.
type WalletInfo struct {
// Name is the display name for the currency, e.g. "Decred"
Expand Down Expand Up @@ -227,6 +233,17 @@ type Wallet interface {
RegFeeConfirmations(ctx context.Context, coinID dex.Bytes) (confs uint32, err error)
}

// TokenMaster is implemented by assets which support degenerate tokens.
type TokenMaster interface {
// CreateTokenWallet creates a wallet for the specified token asset. The
// settings correspond to the Token.Definition.ConfigOpts.
CreateTokenWallet(assetID uint32, settings map[string]string) error
// OpenTokenWallet opens a wallet for the specified token asset. The
// settings correspond to the Token.Definition.ConfigOpts. The tipChange
// function will be called after the parent's tipChange function.
OpenTokenWallet(assetID uint32, settings map[string]string, tipChange func(error)) (Wallet, error)
}

// Balance is categorized information about a wallet's balance.
type Balance struct {
// Available is the balance that is available for trading immediately.
Expand Down
Loading

0 comments on commit eeddb7e

Please sign in to comment.