Commit 8f61f8f0 authored by luxq's avatar luxq

add code

parent ddb1235e
FROM golang:1.22-alpine AS build
# Install dependencies
RUN apk update && \
apk upgrade && \
apk add --no-cache bash git openssh make build-base
WORKDIR /build
ADD . /build/code
RUN cd /build/code && make && cp build/bin/dexbackend /dexbackend
FROM alpine
WORKDIR /root
COPY --from=build /dexbackend /usr/bin/dexbackend
#COPY ./config.yml /root/config.yml
ENTRYPOINT [ "dexbackend" ]
\ No newline at end of file
.PHONY: default swapper docker
GOBIN = $(shell pwd)/build/bin
GO ?= latest
GOFILES_NOVENDOR := $(shell go list -f "{{.Dir}}" ./...)
TAG=latest
default: swapper
all: swapper docker
swag:
swag init -g openapi/server.go
swapper:
go build -o=${GOBIN}/$@ -gcflags "all=-N -l" .
@echo "Done building."
docker:
docker build -t swapper:${TAG} .
clean:
rm -fr build/*
[
{"address":"","private":"0x"},
{"address":"","private":"0x"},
{"address":"","private":"0x"}
]
\ No newline at end of file
package clientpool
import (
"github.com/ethereum/go-ethereum/ethclient"
"sync"
)
// use atomic pool
type poolConfig struct {
RPCNode string
Index int32
clients []*ethclient.Client
mux sync.Mutex
}
var pconfig = &poolConfig{}
func InitPool(cnt int, rpc string) {
pconfig.Index = 0
pconfig.RPCNode = rpc
pconfig.clients = make([]*ethclient.Client, 0)
for i := 0; i < cnt; i++ {
c, _ := ethclient.Dial(pconfig.RPCNode)
pconfig.clients = append(pconfig.clients, c)
}
}
func (p *poolConfig) getClient() *ethclient.Client {
p.mux.Lock()
defer p.mux.Unlock()
p.Index += 1
idx := p.Index % int32(len(p.clients))
c := p.clients[idx]
return c
}
func GetClient() *ethclient.Client {
return pconfig.getClient()
}
package root
import (
"code.wuban.net.cn/service/pancakeswapper/config"
"code.wuban.net.cn/service/pancakeswapper/swapper"
"fmt"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/spf13/viper"
"os"
"strings"
)
var (
cfgFile string
rootCmd = &cobra.Command{
Use: "swapper [command]",
Short: "Swapper is a script for pancake.",
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
bindFlags(cmd)
return config.InitConfig(cfgFile)
},
Run: runCommand,
}
)
func Execute() {
if err := rootCmd.Execute(); err != nil {
_, _ = fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
func init() {
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is ./config.yaml)")
// add param config
rootCmd.PersistentFlags().Int64("param.volume", config.DefaultConfig.Param.Volume, "volume")
rootCmd.PersistentFlags().Int("param.count", config.DefaultConfig.Param.Count, "count")
rootCmd.PersistentFlags().Int64("param.gas_price", config.DefaultConfig.Param.GasPrice, "gas price")
rootCmd.PersistentFlags().Int64("param.slippage", config.DefaultConfig.Param.Slippage, "slippage")
rootCmd.PersistentFlags().Int64("param.deadline", config.DefaultConfig.Param.Deadline, "deadline")
// users config
rootCmd.PersistentFlags().String("users", config.DefaultConfig.Users, "users file")
rootCmd.PersistentFlags().String("log.level", config.DefaultConfig.Log.Level, "Log level")
}
func bindFlags(cmd *cobra.Command) {
cmd.Flags().VisitAll(func(f *pflag.Flag) {
if strings.Contains(f.Name, ".") {
envVar := strings.ToUpper(strings.ReplaceAll(f.Name, ".", "_"))
viper.BindEnv(f.Name, envVar)
}
viper.BindPFlag(f.Name, f)
})
}
func runCommand(cmd *cobra.Command, _ []string) {
cfg := config.Global
setlog(cfg.Log.Level)
log.WithField("config", cfg).Info("load config success")
server, err := swapper.NewSwapper(cfg)
if err != nil {
log.Fatal(err)
}
server.Run()
log.Info("app exit, bye bye !!!")
return
}
func setlog(level string) {
log.SetFormatter(&log.TextFormatter{
DisableColors: true,
FullTimestamp: true,
})
switch level {
case "debug":
log.SetLevel(log.DebugLevel)
case "info":
log.SetLevel(log.InfoLevel)
case "warn":
log.SetLevel(log.WarnLevel)
case "error":
log.SetLevel(log.ErrorLevel)
default:
log.SetLevel(log.DebugLevel)
}
}
param:
# routine count.
routine: 10
# swap volume for each transaction, unit is usdt.
volume: 1
# count of transactions per user.
count: 2
# gas_price unit is Gwei.
gas_price: 1
# slippage, unit is 1/1000. example: 20 means 2.0%.
slippage: 20
# tx deadline, unit is minutes, set 0 to disable.
deadline: 20
rpc:
env: "bsc"
pool: 20
log:
level: "debug"
users: "accounts.json"
\ No newline at end of file
package config
import (
"fmt"
"github.com/mitchellh/mapstructure"
log "github.com/sirupsen/logrus"
"github.com/spf13/viper"
"reflect"
"strings"
)
type Config struct {
Log LogConfig `yaml:"log"`
Param ParamConfig `yaml:"param"`
Rpc RpcConfig `yaml:"rpc"`
Users string `yaml:"users"`
}
type ParamConfig struct {
Routine int `yaml:"routine"`
Volume int64 `yaml:"volume"`
Count int `yaml:"count"`
GasPrice int64 `yaml:"gas_price"`
Slippage int64 `yaml:"slippage"`
Deadline int64 `yaml:"deadline"`
}
type RpcConfig struct {
Env string `yaml:"env"`
PoolSize int `yaml:"pool"`
}
type LogConfig struct {
Level string `yaml:"level"`
}
var Global *Config
func InitConfig(cfgFile string) error {
setDefaultConfig()
if cfgFile != "" {
viper.SetConfigFile(cfgFile)
} else {
viper.AddConfigPath(".")
viper.SetConfigName("config")
viper.SetConfigType("yaml")
}
viper.SetEnvPrefix("SWAPPER")
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
viper.AutomaticEnv()
if err := viper.ReadInConfig(); err != nil {
if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
return fmt.Errorf("failed to read config file: %w", err)
}
log.Warn("No config file found, using defaults")
}
decoderConfig := &mapstructure.DecoderConfig{
Result: &Global,
TagName: "yaml",
WeaklyTypedInput: true,
}
decoder, err := mapstructure.NewDecoder(decoderConfig)
if err != nil {
return fmt.Errorf("failed to create decoder: %w", err)
}
if err := decoder.Decode(viper.AllSettings()); err != nil {
return fmt.Errorf("failed to decode config: %w", err)
}
if err := Global.Validate(); err != nil {
return fmt.Errorf("config validation failed: %w", err)
}
return nil
}
// setDefaultConfig
func setDefaultConfig() {
defaultValues := structToMap(DefaultConfig)
for key, value := range defaultValues {
viper.SetDefault(key, value)
}
}
func structToMap(obj interface{}) map[string]interface{} {
result := make(map[string]interface{})
fillMap(reflect.ValueOf(obj), "", result)
return result
}
func fillMap(v reflect.Value, prefix string, result map[string]interface{}) {
t := v.Type()
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
value := v.Field(i)
tag := field.Tag.Get("yaml")
if tag == "" {
tag = strings.ToLower(field.Name)
}
key := tag
if prefix != "" {
key = prefix + "." + tag
}
if value.Kind() == reflect.Struct {
fillMap(value, key, result)
} else {
result[key] = value.Interface()
}
}
}
func (c *Config) Validate() error {
return nil
}
func (c *Config) GetRootDir() string {
return "./"
}
package config
import (
"fmt"
"testing"
)
func TestInitConfig(t *testing.T) {
f := "../config.yml"
err := InitConfig(f)
if err != nil {
t.Fatalf("failed to init config: %v", err)
}
fmt.Println("param", Global.Param)
fmt.Println("log", Global.Log)
fmt.Println("users", Global.Users)
}
package config
var DefaultConfig = Config{
Log: LogConfig{
Level: "info",
},
Param: ParamConfig{
Volume: 0,
Count: 1,
GasPrice: 1,
Slippage: 20,
Deadline: 20,
},
Users: "accounts.json",
}
#!/bin/bash
# build abi.json to go package.
abigen --abi=param.json --pkg=buildparam --type=ParamContract --out=contract.go
\ No newline at end of file
This diff is collapsed.
[
{
"inputs": [
{
"components": [
{
"components": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "uint160",
"name": "amount",
"type": "uint160"
},
{
"internalType": "uint48",
"name": "expiration",
"type": "uint48"
},
{
"internalType": "uint48",
"name": "nonce",
"type": "uint48"
}
],
"internalType": "struct IPancake.PermitDetails",
"name": "details",
"type": "tuple"
},
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "sigDeadline",
"type": "uint256"
}
],
"internalType": "struct IPancake.PermitSingle",
"name": "permitSingle",
"type": "tuple"
},
{
"internalType": "bytes",
"name": "data",
"type": "bytes"
}
],
"name": "buildParamPermit2",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"internalType": "uint256",
"name": "amountIn",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "amountOutMin",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "path",
"type": "bytes"
},
{
"internalType": "bool",
"name": "playerIsUser",
"type": "bool"
}
],
"name": "buildParamV3_SWAP_EXACT_IN",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes",
"name": "commands",
"type": "bytes"
},
{
"internalType": "bytes[]",
"name": "inputs",
"type": "bytes[]"
},
{
"internalType": "uint256",
"name": "deadline",
"type": "uint256"
}
],
"name": "execute",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]
\ No newline at end of file
#!/bin/bash
# build abi.json to go package.
abigen --abi=erc20.json --pkg=erc20 --type=TokenContract --out=contract.go
\ No newline at end of file
This diff is collapsed.
[
{
"inputs": [
{
"internalType": "string",
"name": "_name",
"type": "string"
},
{
"internalType": "string",
"name": "_symbol",
"type": "string"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "InvalidShortString",
"type": "error"
},
{
"inputs": [
{
"internalType": "string",
"name": "str",
"type": "string"
}
],
"name": "StringTooLong",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "Approval",
"type": "event"
},
{
"anonymous": false,
"inputs": [],
"name": "EIP712DomainChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "previousOwner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "OwnershipTransferred",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "from",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "to",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "Transfer",
"type": "event"
},
{
"inputs": [],
"name": "DOMAIN_SEPARATOR",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "address",
"name": "spender",
"type": "address"
}
],
"name": "allowance",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "approve",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "balanceOf",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "decimals",
"outputs": [
{
"internalType": "uint8",
"name": "",
"type": "uint8"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "subtractedValue",
"type": "uint256"
}
],
"name": "decreaseAllowance",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "eip712Domain",
"outputs": [
{
"internalType": "bytes1",
"name": "fields",
"type": "bytes1"
},
{
"internalType": "string",
"name": "name",
"type": "string"
},
{
"internalType": "string",
"name": "version",
"type": "string"
},
{
"internalType": "uint256",
"name": "chainId",
"type": "uint256"
},
{
"internalType": "address",
"name": "verifyingContract",
"type": "address"
},
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
},
{
"internalType": "uint256[]",
"name": "extensions",
"type": "uint256[]"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "addedValue",
"type": "uint256"
}
],
"name": "increaseAllowance",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "mint",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "name",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
}
],
"name": "nonces",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "owner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "deadline",
"type": "uint256"
},
{
"internalType": "uint8",
"name": "v",
"type": "uint8"
},
{
"internalType": "bytes32",
"name": "r",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "s",
"type": "bytes32"
}
],
"name": "permit",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "renounceOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "symbol",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "totalSupply",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "transfer",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "from",
"type": "address"
},
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "transferFrom",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "transferOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]
\ No newline at end of file
#!/bin/bash
# build abi.json to go package.
abigen --abi=multicall.json --pkg=multicall --type=MulticallContract --out=contract.go
\ No newline at end of file
This diff is collapsed.
[
{
"inputs": [
{
"components": [
{
"internalType": "address",
"name": "target",
"type": "address"
},
{
"internalType": "bytes",
"name": "callData",
"type": "bytes"
}
],
"internalType": "struct Multicall3.Call[]",
"name": "calls",
"type": "tuple[]"
}
],
"name": "aggregate",
"outputs": [
{
"internalType": "uint256",
"name": "blockNumber",
"type": "uint256"
},
{
"internalType": "bytes[]",
"name": "returnData",
"type": "bytes[]"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"internalType": "address",
"name": "target",
"type": "address"
},
{
"internalType": "bool",
"name": "allowFailure",
"type": "bool"
},
{
"internalType": "bytes",
"name": "callData",
"type": "bytes"
}
],
"internalType": "struct Multicall3.Call3[]",
"name": "calls",
"type": "tuple[]"
}
],
"name": "aggregate3",
"outputs": [
{
"components": [
{
"internalType": "bool",
"name": "success",
"type": "bool"
},
{
"internalType": "bytes",
"name": "returnData",
"type": "bytes"
}
],
"internalType": "struct Multicall3.Result[]",
"name": "returnData",
"type": "tuple[]"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"internalType": "address",
"name": "target",
"type": "address"
},
{
"internalType": "bool",
"name": "allowFailure",
"type": "bool"
},
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "callData",
"type": "bytes"
}
],
"internalType": "struct Multicall3.Call3Value[]",
"name": "calls",
"type": "tuple[]"
}
],
"name": "aggregate3Value",
"outputs": [
{
"components": [
{
"internalType": "bool",
"name": "success",
"type": "bool"
},
{
"internalType": "bytes",
"name": "returnData",
"type": "bytes"
}
],
"internalType": "struct Multicall3.Result[]",
"name": "returnData",
"type": "tuple[]"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"internalType": "address",
"name": "target",
"type": "address"
},
{
"internalType": "bytes",
"name": "callData",
"type": "bytes"
}
],
"internalType": "struct Multicall3.Call[]",
"name": "calls",
"type": "tuple[]"
}
],
"name": "blockAndAggregate",
"outputs": [
{
"internalType": "uint256",
"name": "blockNumber",
"type": "uint256"
},
{
"internalType": "bytes32",
"name": "blockHash",
"type": "bytes32"
},
{
"components": [
{
"internalType": "bool",
"name": "success",
"type": "bool"
},
{
"internalType": "bytes",
"name": "returnData",
"type": "bytes"
}
],
"internalType": "struct Multicall3.Result[]",
"name": "returnData",
"type": "tuple[]"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [],
"name": "getBasefee",
"outputs": [
{
"internalType": "uint256",
"name": "basefee",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "blockNumber",
"type": "uint256"
}
],
"name": "getBlockHash",
"outputs": [
{
"internalType": "bytes32",
"name": "blockHash",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getBlockNumber",
"outputs": [
{
"internalType": "uint256",
"name": "blockNumber",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getChainId",
"outputs": [
{
"internalType": "uint256",
"name": "chainid",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getCurrentBlockCoinbase",
"outputs": [
{
"internalType": "address",
"name": "coinbase",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getCurrentBlockDifficulty",
"outputs": [
{
"internalType": "uint256",
"name": "difficulty",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getCurrentBlockGasLimit",
"outputs": [
{
"internalType": "uint256",
"name": "gaslimit",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getCurrentBlockTimestamp",
"outputs": [
{
"internalType": "uint256",
"name": "timestamp",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "addr",
"type": "address"
}
],
"name": "getEthBalance",
"outputs": [
{
"internalType": "uint256",
"name": "balance",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getLastBlockHash",
"outputs": [
{
"internalType": "bytes32",
"name": "blockHash",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bool",
"name": "requireSuccess",
"type": "bool"
},
{
"components": [
{
"internalType": "address",
"name": "target",
"type": "address"
},
{
"internalType": "bytes",
"name": "callData",
"type": "bytes"
}
],
"internalType": "struct Multicall3.Call[]",
"name": "calls",
"type": "tuple[]"
}
],
"name": "tryAggregate",
"outputs": [
{
"components": [
{
"internalType": "bool",
"name": "success",
"type": "bool"
},
{
"internalType": "bytes",
"name": "returnData",
"type": "bytes"
}
],
"internalType": "struct Multicall3.Result[]",
"name": "returnData",
"type": "tuple[]"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bool",
"name": "requireSuccess",
"type": "bool"
},
{
"components": [
{
"internalType": "address",
"name": "target",
"type": "address"
},
{
"internalType": "bytes",
"name": "callData",
"type": "bytes"
}
],
"internalType": "struct Multicall3.Call[]",
"name": "calls",
"type": "tuple[]"
}
],
"name": "tryBlockAndAggregate",
"outputs": [
{
"internalType": "uint256",
"name": "blockNumber",
"type": "uint256"
},
{
"internalType": "bytes32",
"name": "blockHash",
"type": "bytes32"
},
{
"components": [
{
"internalType": "bool",
"name": "success",
"type": "bool"
},
{
"internalType": "bytes",
"name": "returnData",
"type": "bytes"
}
],
"internalType": "struct Multicall3.Result[]",
"name": "returnData",
"type": "tuple[]"
}
],
"stateMutability": "payable",
"type": "function"
}
]
\ No newline at end of file
package contracts
import (
"code.wuban.net.cn/service/pancakeswapper/contracts/v3pool"
"context"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
"math/big"
"testing"
)
func TestGetPrice(t *testing.T) {
client, _ := ethclient.Dial("https://bscnode.bitheart.org")
user := common.HexToAddress("0x3e3b4d16ce35840c28f90edb7f38e5bdd976c7e3")
pool := common.HexToAddress("0xcB0AE3B8337f0Cc35563e6b9fC357F2298C0D24a")
contract, err := v3pool.NewV3Pool(pool, client)
if err != nil {
t.Fatal(err)
}
callOpts := &bind.CallOpts{
BlockNumber: nil,
From: user,
Context: context.Background(),
}
info, err := contract.Slot0(callOpts)
if err != nil {
t.Fatal(err)
}
t.Log("sqrtPriceX96 is", info.SqrtPriceX96.String())
// 获取slot0数据,包含价格sqrtPriceX96和tick
var slot0 = info
var sqrtPriceX96 = slot0.SqrtPriceX96
//var tick = slot0.Tick
priceX96 := new(big.Int).Mul(sqrtPriceX96, sqrtPriceX96)
//priceX96Dec := priceX96
dec := big.NewInt(1e18)
priceX96Dec := new(big.Int).Mul(priceX96, dec)
price := priceX96Dec.Div(priceX96Dec, new(big.Int).Exp(big.NewInt(2), big.NewInt(192), nil))
bf := new(big.Float).SetInt(price)
priceFloat, _ := bf.Float64()
priceFloat = priceFloat / 1e18
t.Log("price is", priceFloat)
}
#!/bin/bash
# build abi.json to go package.
abigen --abi=v3pool.json --pkg=v3pool --type=V3Pool --out=contract.go
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
module code.wuban.net.cn/service/pancakeswapper
go 1.22.8
require (
github.com/adshao/go-binance/v2 v2.6.1
github.com/ethereum/go-ethereum v1.14.11
github.com/gin-gonic/gin v1.10.0
github.com/go-redis/redis/v8 v8.11.5
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da
github.com/mitchellh/mapstructure v1.5.0
github.com/shopspring/decimal v1.4.0
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.8.1
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.19.0
github.com/stretchr/testify v1.9.0
github.com/swaggo/files v1.0.1
github.com/swaggo/gin-swagger v1.6.0
github.com/swaggo/swag v1.8.12
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7
gorm.io/driver/mysql v1.5.7
gorm.io/gorm v1.25.12
)
require (
github.com/KyleBanks/depth v1.2.1 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/PuerkitoBio/purell v1.1.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/StackExchange/wmi v1.2.1 // indirect
github.com/bitly/go-simplejson v0.5.0 // indirect
github.com/bits-and-blooms/bitset v1.13.0 // indirect
github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect
github.com/bytedance/sonic v1.11.6 // indirect
github.com/bytedance/sonic/loader v0.1.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cloudwego/base64x v0.1.4 // indirect
github.com/cloudwego/iasm v0.2.0 // indirect
github.com/consensys/bavard v0.1.13 // indirect
github.com/consensys/gnark-crypto v0.12.1 // indirect
github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c // indirect
github.com/crate-crypto/go-kzg-4844 v1.0.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/deckarep/golang-set/v2 v2.6.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/ethereum/c-kzg-4844 v1.0.0 // indirect
github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.19.6 // indirect
github.com/go-openapi/spec v0.20.4 // indirect
github.com/go-openapi/swag v0.19.15 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.20.0 // indirect
github.com/go-sql-driver/mysql v1.7.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/holiman/uint256 v1.3.1 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mailru/easyjson v0.7.6 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mmcloughlin/addchain v0.4.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/sagikazarmark/locafero v0.6.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.7.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/supranational/blst v0.3.13 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/arch v0.8.0 // indirect
golang.org/x/crypto v0.28.0 // indirect
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect
golang.org/x/net v0.30.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.26.0 // indirect
golang.org/x/text v0.19.0 // indirect
golang.org/x/tools v0.26.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
rsc.io/tmplfunc v0.0.3 // indirect
)
This diff is collapsed.
package grouptask
import (
"code.wuban.net.cn/service/pancakeswapper/types"
"errors"
"sync"
)
var (
ErrTaskPoolIsFull = errors.New("task pool is full")
)
type TaskHandle func(acc *types.Account, env types.Env, idx uint)
type Tasks struct {
tasknum uint
handler TaskHandle
taskpool chan *types.Account
env types.Env
wg sync.WaitGroup
}
func NewTasks(routine uint, env types.Env, handle TaskHandle) *Tasks {
return &Tasks{
tasknum: routine,
handler: handle,
env: env,
taskpool: make(chan *types.Account, 1000000),
}
}
func (t *Tasks) AddTask(task *types.Account) error {
select {
case t.taskpool <- task:
return nil
default:
return ErrTaskPoolIsFull
}
}
func (t *Tasks) WaitFinish() {
close(t.taskpool)
t.wg.Wait()
}
func (t *Tasks) Run() {
for i := uint(0); i < t.tasknum; i++ {
t.wg.Add(1)
go func(chanIdx uint) {
defer t.wg.Done()
for {
select {
case task, ok := <-t.taskpool:
if !ok {
return
}
t.handler(task, t.env, chanIdx)
}
}
}(i)
}
}
func DoMultiTasks(routine int, env types.Env, handler TaskHandle, items []*types.Account) {
tasks := NewTasks(uint(routine), env, handler)
tasks.Run()
for _, item := range items {
tasks.AddTask(item)
}
tasks.WaitFinish()
}
package main
import "code.wuban.net.cn/service/pancakeswapper/command/root"
func main() {
root.Execute()
}
package pancake
import (
ethcommon "github.com/ethereum/go-ethereum/common"
"math/big"
)
type Commands byte
const (
V3_SWAP_EXACT_IN Commands = 0x00
V3_SWAP_EXACT_OUT Commands = 0x01
PERMIT2_TRANSFER_FROM Commands = 0x02
PERMIT2_PERMIT_BATCH Commands = 0x03
SWEEP Commands = 0x04
TRANSFER Commands = 0x05
PAY_PORTION Commands = 0x06
V2_SWAP_EXACT_IN Commands = 0x08
V2_SWAP_EXACT_OUT Commands = 0x09
PERMIT2_PERMIT Commands = 0x0a
WRAP_ETH Commands = 0x0b
UNWRAP_WETH Commands = 0x0c
PERMIT2_TRANSFER_FROM_BATCH Commands = 0x0d
BALANCE_CHECK_ERC20 Commands = 0x0e
INFI_SWAP Commands = 0x10
V3_POSITION_MANAGER_PERMIT Commands = 0x11
V3_POSITION_MANAGER_CALL Commands = 0x12
INFI_CL_INITIALIZE_POOL Commands = 0x13
INFI_BIN_INITIALIZE_POOL Commands = 0x14
INFI_CL_POSITION_CALL Commands = 0x15
INFI_BIN_POSITION_CALL Commands = 0x16
// COMMAND_PLACEHOLDER = 0x17 -> 0x20
// Command Types where 0x21<=value<=0x3f
EXECUTE_SUB_PLAN Commands = 0x21
STABLE_SWAP_EXACT_IN Commands = 0x22
STABLE_SWAP_EXACT_OUT Commands = 0x23
)
type CallParam struct {
Cmd Commands
Param interface{}
}
type ParamV3SwapExactIn struct {
Recipient ethcommon.Address
AmountIn *big.Int
AmountOutMin *big.Int
Path []byte
PayerIsUser bool
}
package pancake
import (
"bytes"
"encoding/hex"
"fmt"
"github.com/ethereum/go-ethereum/accounts/abi"
"math/big"
"testing"
"github.com/ethereum/go-ethereum/common"
)
type param struct {
Recipient common.Address
AmountIn *big.Int
AmountOutMin *big.Int
Path []byte
PayerIsUser bool
}
func TestAbiDecode(t *testing.T) {
// ABI definition for the struct
const abiJSON = `[{
"components": [
{ "name": "recipient", "type": "address" },
{ "name": "amountIn", "type": "uint256" },
{ "name": "amountOutMin", "type": "uint256" },
{ "name": "path", "type": "bytes" },
{ "name": "payerIsUser", "type": "bool" }
],
"name": "ParamV3SwapExactIn",
"type": "tuple"
}]`
abiReader := bytes.NewReader([]byte(abiJSON))
// ABI parsing
parsedABI, err := abi.JSON(abiReader)
if err != nil {
t.Fatalf("Failed to parse ABI: %v", err)
}
// ABI-encoded data
data := "0x0000000000000000000000003e3b4d16ce35840c28f90edb7f38e5bdd976c7e30000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000012d0ee4f5819f97200000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002b55d398326f99059ff775485246999027b3197955002710215324115417a13879f865dc5bb651b18a781a59000000000000000000000000000000000000000000"
// Decode the data
decodedData, err := hex.DecodeString(data[2:]) // Remove "0x" prefix
if err != nil {
t.Fatalf("Failed to decode hex data: %v", err)
}
// Unpack the data into the struct
var param ParamV3SwapExactIn
err = parsedABI.UnpackIntoInterface(&param, "ParamV3SwapExactIn", decodedData)
if err != nil {
t.Fatalf("Failed to unpack data: %v", err)
}
// Print the decoded struct
fmt.Printf("Recipient: %s\n", param.Recipient.Hex())
fmt.Printf("AmountIn: %s\n", param.AmountIn.String())
fmt.Printf("AmountOutMin: %s\n", param.AmountOutMin.String())
fmt.Printf("Path: %x\n", param.Path)
fmt.Printf("PayerIsUser: %t\n", param.PayerIsUser)
}
package pancake
import (
"code.wuban.net.cn/service/pancakeswapper/contracts/buildparam"
"code.wuban.net.cn/service/pancakeswapper/contracts/v3pool"
"code.wuban.net.cn/service/pancakeswapper/types"
"fmt"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
"math/big"
"strings"
"time"
)
var (
tokenDec = big.NewInt(1e18)
TransferTopic = common.HexToHash("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")
)
func leftPadding(data []byte, length int) []byte {
if len(data) >= length {
return data
}
padding := make([]byte, length-len(data))
return append(padding, data...)
}
func MakeV3SwapExactInPathData(env types.Env) []byte {
//0000000000000000000000003e3b4d16ce35840c28f90edb7f38e5bdd976c7e3
//0000000000000000000000000000000000000000000000001bc16d674ec80000
//0000000000000000000000000000000000000000000000002620b5d4cdab6c04
//00000000000000000000000000000000000000000000000000000000000000a0
//0000000000000000000000000000000000000000000000000000000000000001
//000000000000000000000000000000000000000000000000000000000000002b
//55d398326f99059ff775485246999027b3197955002710215324115417a13879f865dc5bb651b18a781a59000000000000000000000000000000000000000000
token0 := common.HexToAddress(env.USDT)
token1 := common.HexToAddress(env.KLKO)
fee := big.NewInt(10000)
data := make([]byte, 0)
data = append(data, token0.Bytes()...)
data = append(data, leftPadding(fee.Bytes(), 3)...)
data = append(data, token1.Bytes()...)
return data
}
func MakeV3SwapExactInData(env types.Env, param ParamV3SwapExactIn) ([]byte, error) {
//0000000000000000000000003e3b4d16ce35840c28f90edb7f38e5bdd976c7e3
//0000000000000000000000000000000000000000000000000de0b6b3a7640000
//00000000000000000000000000000000000000000000000012d0ee4f5819f972
//00000000000000000000000000000000000000000000000000000000000000a0
//0000000000000000000000000000000000000000000000000000000000000001
//000000000000000000000000000000000000000000000000000000000000002b
//55d398326f99059ff775485246999027b3197955002710215324115417a13879
//f865dc5bb651b18a781a59000000000000000000000000000000000000000000
param.Path = MakeV3SwapExactInPathData(env)
buildAbi, _ := abi.JSON(strings.NewReader(buildparam.ParamContractMetaData.ABI))
data, err := buildAbi.Pack("buildParamV3_SWAP_EXACT_IN", param.Recipient, param.AmountIn, param.AmountOutMin, param.Path, param.PayerIsUser)
if err != nil {
return nil, err
}
// remove method id.
data = data[4:]
return data, nil
}
func MakePermit2PermitData(acc *types.Account, env types.Env) ([]byte, error) {
// build PERMIT2_PERMIT input.
year := int64(365 * 24 * 60 * 60)
hour := int64(60 * 60)
permitSingle := buildparam.IPancakePermitSingle{
Details: buildparam.IPancakePermitDetails{
Token: common.HexToAddress(env.USDT),
Amount: abi.MaxUint256,
Expiration: big.NewInt(time.Now().Unix() + year),
Nonce: big.NewInt(time.Now().Unix()),
},
Spender: common.HexToAddress(env.Router),
SigDeadline: big.NewInt(time.Now().Unix() + hour),
}
signature, _ := acc.SignMessage([]byte("permit2"))
buildAbi, _ := abi.JSON(strings.NewReader(buildparam.ParamContractMetaData.ABI))
data, err := buildAbi.Pack("buildParamPermit2", permitSingle, signature)
if err != nil {
return nil, err
}
// remove method id.
data = data[4:]
return data, nil
}
// GetAmountOut calculates the amount out for a given amount in, slippage, and fee.
// It uses the Uniswap V3 pool contract to get the current price and calculates
// the amount out based on the input parameters.
// amountIn is the amount of input token (USDT) to swap.
// slippage is the maximum slippage allowed for the swap, the value is percent mul 1000, eg: 20 is 2%.
// feeRate is the fee charged by the pool, the value is percent mul 1000000, eg: 10000 is 1%.
func GetAmountOutMin(client *ethclient.Client, env types.Env, amountIn *big.Int,
slippage int64, feeRate *big.Int) (*big.Int, error) {
// get slot0 data.
pool := common.HexToAddress(env.Pool)
contract, err := v3pool.NewV3Pool(pool, client)
if err != nil {
return nil, err
}
callOpts := &bind.CallOpts{
BlockNumber: nil,
From: common.HexToAddress(env.USDT),
Context: nil,
}
slot0, err := contract.Slot0(callOpts)
if err != nil {
return nil, err
}
sqrtPriceX96 := slot0.SqrtPriceX96
priceX96 := new(big.Int).Mul(sqrtPriceX96, sqrtPriceX96)
priceX96Dec := new(big.Int).Mul(priceX96, tokenDec)
price := priceX96Dec.Div(priceX96Dec, new(big.Int).Exp(big.NewInt(2), big.NewInt(2*96), nil))
amountIn = new(big.Int).Mul(amountIn, tokenDec)
fee := new(big.Int).Div(new(big.Int).Mul(amountIn, feeRate), big.NewInt(1e6))
amountIn = amountIn.Sub(amountIn, fee)
fmt.Println("sub fee", amountIn.Text(10))
amountOut := new(big.Int).Div(amountIn, price)
fmt.Println("amount out", amountOut.Text(10))
minAmountOut := new(big.Int).Mul(amountOut, big.NewInt(1000-slippage))
minAmountOut = minAmountOut.Div(minAmountOut, big.NewInt(1000))
minAmountOut = minAmountOut.Mul(minAmountOut, tokenDec)
return minAmountOut, nil
}
package pancake
import (
"code.wuban.net.cn/service/pancakeswapper/types"
"encoding/hex"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
"math/big"
"testing"
)
func TestMakeV3SwapExactInPathData(t *testing.T) {
env := types.GetEnv("bsc")
data := MakeV3SwapExactInPathData(env)
t.Log(hex.EncodeToString(data))
}
func TestMakeV3SwapExactInData(t *testing.T) {
env := types.GetEnv("bsc")
p := ParamV3SwapExactIn{
Recipient: common.HexToAddress("3e3b4d16ce35840c28f90edb7f38e5bdd976c7e3"),
AmountIn: big.NewInt(103400000000000),
AmountOutMin: big.NewInt(13211344443399),
Path: nil,
PayerIsUser: true,
}
data, err := MakeV3SwapExactInData(env, p)
if err != nil {
t.Fatal(err)
}
t.Log(hex.EncodeToString(data))
}
func TestGetAmountOutMin(t *testing.T) {
client, _ := ethclient.Dial("https://bscnode.bitheart.org")
env := types.GetEnv("bsc")
in := int64(10000)
out, err := GetAmountOutMin(client, env, big.NewInt(in), 10, big.NewInt(10000))
if err != nil {
t.Fatal(err)
}
bfOut := new(big.Float).SetInt(out)
v, _ := bfOut.Float64()
t.Logf("in:%d, out: %f", in, v/1e18)
}
package swapper
import (
"code.wuban.net.cn/service/pancakeswapper/clientpool"
"code.wuban.net.cn/service/pancakeswapper/config"
"code.wuban.net.cn/service/pancakeswapper/grouptask"
"code.wuban.net.cn/service/pancakeswapper/types"
"code.wuban.net.cn/service/pancakeswapper/work"
"fmt"
"time"
)
type Swapper struct {
cfg *config.Config
}
func NewSwapper(conf *config.Config) (*Swapper, error) {
n := new(Swapper)
n.cfg = conf
return n, nil
}
func (s *Swapper) Run() {
env := types.GetEnv(s.cfg.Rpc.Env)
clientpool.InitPool(s.cfg.Rpc.PoolSize, env.RPC)
work.WorkInit(env.ChainId)
// 1. read account info from json file
accs := work.LoadAccounts(s.cfg.Users)
work.SetTotalUser(len(accs))
waitCh := make(chan struct{})
// 2. init account info
go func(w chan struct{}, accounts []*types.Account) {
grouptask.DoMultiTasks(s.cfg.Param.Routine, env, work.HandlerAccountSwap, accounts)
w <- struct{}{}
}(waitCh, accs)
tm := time.NewTicker(time.Second * time.Duration(2))
defer tm.Stop()
for {
select {
case <-tm.C:
// print progress
fmt.Println(work.GetReport())
fmt.Println("")
case <-waitCh:
failed := work.Finish()
if failed > 0 {
fmt.Printf("all done, %d failed user info write in failed.json\n", failed)
} else {
fmt.Println("all done, no failed user")
}
return
}
}
}
package types
import (
"crypto/ecdsa"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"math/big"
)
type Account struct {
Address string `json:"address"`
PrivateKey string `json:"private"`
//Nonce uint64 `json:"-"`
PK *ecdsa.PrivateKey `json:"-"`
Addr common.Address `json:"-"`
}
func (a *Account) sign(chainid *big.Int, tx *types.Transaction) (*types.Transaction, error) {
signer := types.NewEIP155Signer(chainid)
h := signer.Hash(tx)
sig, err := crypto.Sign(h[:], a.PK)
if err != nil {
return nil, err
}
return tx.WithSignature(signer, sig)
}
func (a *Account) SignTx(addr common.Address, tx *types.Transaction) (*types.Transaction, error) {
return a.sign(big.NewInt(56), tx)
}
func (a *Account) SignMessage(msg []byte) ([]byte, error) {
sig, err := crypto.Sign(msg, a.PK)
if err != nil {
return nil, err
}
return sig, nil
}
package types
var (
allEnv = make(map[string]Env)
)
func init() {
allEnv["bsc"] = Env{
RPC: "https://bscnode.bitheart.org",
USDT: "0x55d398326f99059ff775485246999027b3197955",
KLKO: "0x215324115417a13879f865Dc5BB651B18A781a59", // KLKO
Permit2: "0x31c2F6fcFf4F8759b3Bd5Bf0e1084A055615c768",
Router: "0x1A0A18AC4BECDDbd6389559687d1A73d8927E416",
Pool: "0xcB0AE3B8337f0Cc35563e6b9fC357F2298C0D24a",
ChainId: 56,
}
}
type Env struct {
RPC string
USDT string
KLKO string
Permit2 string
Router string
Pool string
ChainId int64
}
func GetEnv(network string) Env {
return allEnv[network]
}
This diff is collapsed.
package work
import (
"fmt"
"strings"
"sync"
"sync/atomic"
)
type Metric struct {
mux sync.RWMutex
name string
val atomic.Value
}
func NewMetric(name string) *Metric {
m := &Metric{name: name}
m.val.Store(0)
return m
}
func (m *Metric) Incr() {
m.mux.Lock()
defer m.mux.Unlock()
v := m.val.Load().(int)
m.val.Store(v + 1)
}
func (m *Metric) Get() int {
return m.val.Load().(int)
}
func (m *Metric) Set(v int) {
m.mux.Lock()
defer m.mux.Unlock()
m.val.Store(v)
}
func (m *Metric) Add(v int) {
m.mux.Lock()
defer m.mux.Unlock()
current := m.val.Load().(int)
m.val.Store(current + v)
}
var (
totalUser = NewMetric("totalUser")
failedUser = NewMetric("failedUser")
finishedUser = NewMetric("finishedUser")
totalTx = NewMetric("totalTx")
failedTx = NewMetric("failedTx")
finishedTx = NewMetric("finishedTx")
costToken = NewMetric("costToken")
receiveToken = NewMetric("receiveToken")
)
func SetTotalUser(cnt int) {
totalUser.Set(cnt)
}
func IncrFinishedUser() {
finishedUser.Incr()
}
func IncrFailedUser() {
failedUser.Incr()
}
func IncrTotalTx() {
totalTx.Incr()
}
func IncrFailedTx() {
failedTx.Incr()
}
func IncrFinishedTx() {
finishedTx.Incr()
}
func IncrReceiveToken(val int) {
receiveToken.Add(val)
}
func IncrCostToken(val int) {
costToken.Add(val)
}
func GetReport() string {
str := make([]string, 0)
str = append(str, "REPORT\t\t\tFailed/Total\t\t\tFinished/Total")
str = append(str, fmt.Sprintf("USER\t\t\t%5d/%-5d\t\t\t\t%5d/%-5d", failedUser.Get(), totalUser.Get(), finishedUser.Get(), totalUser.Get()))
str = append(str, fmt.Sprintf("TX\t\t\t\t\t\t\t\t%5d/%-5d", finishedTx.Get(), totalTx.Get()))
str = append(str, fmt.Sprintf("%d Cost / %d Receive", costToken.Get(), receiveToken.Get()))
return strings.Join(str, "\n")
}
package work
import (
"fmt"
"testing"
)
func TestGetReport(t *testing.T) {
SetTotalUser(15000)
for i := 0; i < 10000; i++ {
if i%125 == 0 {
IncrFailedUser()
} else {
IncrFinishedUser()
}
IncrTotalTx()
IncrTotalTx()
IncrTotalTx()
IncrTotalTx()
IncrTotalTx()
if i%100 == 0 {
IncrFailedTx()
} else {
IncrFinishedTx()
IncrFinishedTx()
}
if i%1000 == 0 {
fmt.Println(GetReport())
fmt.Println("")
}
}
}
package work
import (
"fmt"
"math/big"
"os"
"sync"
)
var (
logFile = "detail.log"
logIns *os.File
mux sync.Mutex
chainId *big.Int
)
func WorkInit(cid int64) {
ins, err := os.OpenFile(logFile, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0666)
if err != nil {
panic(fmt.Errorf("can't create log file: %v", err))
}
logIns = ins
chainId = big.NewInt(cid)
}
func WriteLog(s string) {
mux.Lock()
defer mux.Unlock()
// write to file
logIns.WriteString(s + "\n")
}
func WriteFinished() {
mux.Lock()
defer mux.Unlock()
logIns.Close()
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment