Skip to content

Commit

Permalink
finish polygon (#2431)
Browse files Browse the repository at this point in the history
* Finish Polygon

Generalize the ethereum wallet. Add tokens.
Implement rpc tests and getgas. Server backend.
Deploy usdc, Some frontend.

---------

Co-authored-by: Jonathan Chappelow <[email protected]>
  • Loading branch information
buck54321 and chappjc committed Sep 6, 2023
1 parent 883c935 commit 3fe9316
Show file tree
Hide file tree
Showing 68 changed files with 3,275 additions and 1,410 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,5 @@ client/asset/btc/electrum/example/server/server
client/asset/btc/electrum/example/wallet/wallet
**/testdata/fuzz
client/asset/eth/cmd/getgas/getgas
client/asset/eth/cmd/deploy/deploy
client/cmd/dexc-desktop/pkg/installers
3 changes: 3 additions & 0 deletions client/app/importlgpl.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@ import (
_ "decred.org/dcrdex/client/asset/eth" // register eth asset
_ "decred.org/dcrdex/client/asset/polygon" // register polygon network
dexeth "decred.org/dcrdex/dex/networks/eth"
dexpolygon "decred.org/dcrdex/dex/networks/polygon"
)

func init() {
dexeth.MaybeReadSimnetAddrs()
dexpolygon.MaybeReadSimnetAddrs()

}
142 changes: 142 additions & 0 deletions client/asset/eth/chaincfg.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
// This code is available on the terms of the project LICENSE.md file,
// also available online at https://blueoakcouncil.org/license/1.0.0.

package eth

import (
"fmt"
"os"
"os/user"
"path/filepath"
"strings"

"decred.org/dcrdex/dex"
dexeth "decred.org/dcrdex/dex/networks/eth"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
ethcore "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/params"
)

// CompatibilityData is some addresses and hashes used for validating an RPC
// APIs compatibility with trading.
type CompatibilityData struct {
Addr common.Address
TokenAddr common.Address
TxHash common.Hash
BlockHash common.Hash
}

var (
mainnetCompatibilityData = CompatibilityData{
// Vitalik's address from https://twitter.com/VitalikButerin/status/1050126908589887488
Addr: common.HexToAddress("0xab5801a7d398351b8be11c439e05c5b3259aec9b"),
TokenAddr: common.HexToAddress("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"),
TxHash: common.HexToHash("0xea1a717af9fad5702f189d6f760bb9a5d6861b4ee915976fe7732c0c95cd8a0e"),
BlockHash: common.HexToHash("0x44ebd6f66b4fd546bccdd700869f6a433ef9a47e296a594fa474228f86eeb353"),
}

testnetCompatibilityData = CompatibilityData{
Addr: common.HexToAddress("0x8879F72728C5eaf5fB3C55e6C3245e97601FBa32"),
TokenAddr: common.HexToAddress("0x07865c6E87B9F70255377e024ace6630C1Eaa37F"),
TxHash: common.HexToHash("0x4e1d455f7eac7e3a5f7c1e0989b637002755eaee3a262f90b0f3aef1f1c4dcf0"),
BlockHash: common.HexToHash("0x8896021c2666303a85b7e4a6a6f2b075bc705d4e793bf374cd44b83bca23ef9a"),
}
)

// NetworkCompatibilityData returns the CompatibilityData for the specified
// network. If using simnet, make sure the simnet harness is running.
func NetworkCompatibilityData(net dex.Network) (c CompatibilityData, err error) {
switch net {
case dex.Mainnet:
return mainnetCompatibilityData, nil
case dex.Testnet:
return testnetCompatibilityData, nil
case dex.Simnet:
default:
return c, fmt.Errorf("No compatibility data for network # %d", net)
}
// simnet
tDir, err := simnetDataDir()
if err != nil {
return
}

addr := common.HexToAddress("18d65fb8d60c1199bb1ad381be47aa692b482605")
var (
tTxHashFile = filepath.Join(tDir, "test_tx_hash.txt")
tBlockHashFile = filepath.Join(tDir, "test_block10_hash.txt")
tContractFile = filepath.Join(tDir, "test_token_contract_address.txt")
)
readIt := func(path string) string {
b, err := os.ReadFile(path)
if err != nil {
panic(fmt.Sprintf("Problem reading simnet testing file %q: %v", path, err))
}
return strings.TrimSpace(string(b)) // mainly the trailing "\r\n"
}
return CompatibilityData{
Addr: addr,
TokenAddr: common.HexToAddress(readIt(tContractFile)),
TxHash: common.HexToHash(readIt(tTxHashFile)),
BlockHash: common.HexToHash(readIt(tBlockHashFile)),
}, nil
}

// simnetDataDir returns the data directory for Ethereum simnet.
func simnetDataDir() (string, error) {
u, err := user.Current()
if err != nil {
return "", fmt.Errorf("error getting current user: %w", err)
}

return filepath.Join(u.HomeDir, "dextest", "eth"), nil
}

// ETHConfig returns the ETH protocol configuration for the specified network.
func ETHConfig(net dex.Network) (c ethconfig.Config, err error) {
c = ethconfig.Defaults
switch net {
// Ethereum
case dex.Testnet:
c.Genesis = core.DefaultGoerliGenesisBlock()
case dex.Mainnet:
c.Genesis = core.DefaultGenesisBlock()
case dex.Simnet:
c.Genesis, err = readSimnetGenesisFile()
if err != nil {
return c, fmt.Errorf("readSimnetGenesisFile error: %w", err)
}
default:
return c, fmt.Errorf("unknown network %d", net)

}
c.NetworkId = c.Genesis.Config.ChainID.Uint64()
return
}

// ChainConfig returns the core configuration for the blockchain.
func ChainConfig(net dex.Network) (c *params.ChainConfig, err error) {
cfg, err := ETHConfig(net)
if err != nil {
return nil, err
}
return cfg.Genesis.Config, nil
}

// readSimnetGenesisFile reads the simnet genesis file.
func readSimnetGenesisFile() (*ethcore.Genesis, error) {
dataDir, err := simnetDataDir()
if err != nil {
return nil, err
}

genesisFile := filepath.Join(dataDir, "genesis.json")
genesisCfg, err := dexeth.LoadGenesisFile(genesisFile)
if err != nil {
return nil, fmt.Errorf("error reading genesis file: %v", err)
}

return genesisCfg, nil
}
194 changes: 194 additions & 0 deletions client/asset/eth/cmd/deploy/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
// This code is available on the terms of the project LICENSE.md file,
// also available online at https://blueoakcouncil.org/license/1.0.0.

package main

/*
deploy is a utility for deploying swap contracts. Examples of use:
1) Estimate the funding required to deploy a base asset swap contract to
Ethereum
./deploy --mainnet --chain eth --fundingrequired
This will use the current prevailing fee rate to estimate the fee
requirements for the deployment transaction. The estimate is only
accurate if there are already enough funds in the wallet (so estimateGas
can be used), otherwise, a generously-padded constant is used to estimate
the gas requirements.
2) Deploy a base asset swap contract to Ethereum.
./deploy --mainnet --chain eth
3) Deploy a token swap contract to Polygon.
./deploy --mainnet --chain polygon --tokenaddr 0x2791bca1f2de4661ed88a30c99a7a9449aa84174
4) Return remaining Goerli testnet ETH balance to specified address.
./deploy --testnet --chain eth --returnaddr 0x18d65fb8d60c1199bb1ad381be47aa692b482605
IMPORTANT: deploy uses the same wallet configuration as getgas. See getgas
README for instructions.
5) Test reading of the Polygon credentials file.
./deploy --chain polygon --mainnet --readcreds
*/

import (
"context"
"flag"
"fmt"
"os"
"os/user"
"path/filepath"
"strings"

"decred.org/dcrdex/client/asset"
"decred.org/dcrdex/client/asset/eth"
"decred.org/dcrdex/client/asset/polygon"
"decred.org/dcrdex/dex"
dexeth "decred.org/dcrdex/dex/networks/eth"
dexpolygon "decred.org/dcrdex/dex/networks/polygon"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/params"
)

func main() {
if err := mainErr(); err != nil {
fmt.Fprint(os.Stderr, err, "\n")
os.Exit(1)
}
os.Exit(0)
}

func mainErr() error {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

u, err := user.Current()
if err != nil {
return fmt.Errorf("could not get the current user: %w", err)
}
defaultCredentialsPath := filepath.Join(u.HomeDir, "dextest", "credentials.json")

var contractVerI int
var chain, credentialsPath, tokenAddress, returnAddr string
var useTestnet, useMainnet, useSimnet, trace, debug, readCreds, fundingReq bool
flag.BoolVar(&readCreds, "readcreds", false, "does not run gas estimates. read the credentials file and print the address")
flag.BoolVar(&fundingReq, "fundingrequired", false, "does not run gas estimates. calculate the funding required by the wallet to get estimates")
flag.StringVar(&returnAddr, "return", "", "does not run gas estimates. return ethereum funds to supplied address")
flag.BoolVar(&useMainnet, "mainnet", false, "use mainnet")
flag.BoolVar(&useTestnet, "testnet", false, "use testnet")
flag.BoolVar(&useSimnet, "simnet", false, "use simnet")
flag.BoolVar(&trace, "trace", false, "use simnet")
flag.BoolVar(&debug, "debug", false, "use simnet")
flag.StringVar(&chain, "chain", "eth", "symbol of the base chain")
flag.StringVar(&tokenAddress, "tokenaddr", "", "launches an erc20-linked contract with this token. default launches a base chain contract")
flag.IntVar(&contractVerI, "ver", 0, "contract version")
flag.StringVar(&credentialsPath, "creds", defaultCredentialsPath, "path for JSON credentials file.")
flag.Parse()

if !useMainnet && !useTestnet && !useSimnet {
return fmt.Errorf("no network specified. add flag --mainnet, --testnet, or --simnet")
}
if (useMainnet && useTestnet) || (useMainnet && useSimnet) || (useTestnet && useSimnet) {
return fmt.Errorf("more than one network specified")
}

net := dex.Mainnet
if useSimnet {
net = dex.Simnet
dexeth.MaybeReadSimnetAddrs()
dexpolygon.MaybeReadSimnetAddrs()
}
if useTestnet {
net = dex.Testnet
}

if readCreds {
addr, providers, err := eth.GetGas.ReadCredentials(chain, credentialsPath, net)
if err != nil {
return err
}
fmt.Println("Credentials successfully parsed")
fmt.Println("Address:", addr)
fmt.Println("Providers:", strings.Join(providers, ", "))
return nil
}

assetID, found := dex.BipSymbolID(chain)
if !found {
return fmt.Errorf("asset %s not known", chain)
}

if tkn := asset.TokenInfo(assetID); tkn != nil {
return fmt.Errorf("specified chain is not a base chain. appears to be token %s", tkn.Name)
}

contractVer := uint32(contractVerI)

logLvl := dex.LevelInfo
if debug {
logLvl = dex.LevelDebug
}
if trace {
logLvl = dex.LevelTrace
}

var tokenAddr common.Address
if tokenAddress != "" {
if !common.IsHexAddress(tokenAddress) {
return fmt.Errorf("token address %q does not appear to be valid", tokenAddress)
}
tokenAddr = common.HexToAddress(tokenAddress)
}

log := dex.StdOutLogger("DEPLOY", logLvl)

var bui *dex.UnitInfo
var chainCfg *params.ChainConfig
switch chain {
case "eth":
bui = &dexeth.UnitInfo
chainCfg, err = eth.ChainConfig(net)
if err != nil {
return fmt.Errorf("error finding chain config: %v", err)
}
case "polygon":
bui = &dexpolygon.UnitInfo
chainCfg, err = polygon.ChainConfig(net)
if err != nil {
return fmt.Errorf("error finding chain config: %v", err)
}
}

switch {
case fundingReq:
return eth.ContractDeployer.EstimateDeployFunding(
ctx,
chain,
contractVer,
tokenAddr,
credentialsPath,
chainCfg,
bui,
log,
net,
)
case returnAddr != "":
if !common.IsHexAddress(returnAddr) {
return fmt.Errorf("return address %q is not valid", returnAddr)
}
addr := common.HexToAddress(returnAddr)
return eth.ContractDeployer.ReturnETH(ctx, chain, addr, credentialsPath, chainCfg, bui, log, net)
default:
return eth.ContractDeployer.DeployContract(
ctx,
chain,
contractVer,
tokenAddr,
credentialsPath,
chainCfg,
bui,
log,
net,
)
}
}
17 changes: 13 additions & 4 deletions client/asset/eth/cmd/getgas/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,28 @@ If this is a new asset, you must populate either `dexeth.VersionedGases` or
`dexeth.Tokens` with generous estimates before running `getgas`.

### Use
- Create a credentials file. `getgas` will look in `~/ethtest/getgas-credentials.json`. You can override that location with the `--creds` CLI argument. The credentials file should have the JSON format in the example below. The seed can be anything.
- Create a credentials file. `getgas` will look for `~/dextest/credentials.json`.
You can override that location with the `--creds` CLI argument.
The credentials file should have the JSON format in the example below. The seed can be anything.

**example credentials file**
```
{
"seed": "<32-bytes hex>",
"providers": {
"simnet": "http://localhost:38556",
"testnet": "https://goerli.infura.io/v3/<API key>",
"mainnet": "https://mainnet.infura.io/v3/<API key>"
"eth": {
"testnet: [
"https://goerli.infura.io/v3/<API key>"
],
"mainnet": [
"https://mainnet.infura.io/v3/<API key>"
]
},
"polygon": { ... }
}
}
```
- Select the blockchain with `--chain`. The default is `--chain eth`, but `--chain polygon` can be selected as well.

- Use the `--readcreds` utility to check the validity of the credentials file and to print the address. e.g. `./getgas --readcreds --mainnet`.

Expand Down
Loading

0 comments on commit 3fe9316

Please sign in to comment.