From 7731a23e35dac90bc6d1170185d610952ebe5aa5 Mon Sep 17 00:00:00 2001 From: Mohamed Sohail Date: Sun, 8 May 2022 14:13:45 +0300 Subject: [PATCH] update: (cic_net) add new methods and tests (#1) * update: (cic_net) add new methods and tests * add: write tx methods and tests - add ci for testing - coveralls for coverage * ci: add test environment * ci: expose secrets to env * docs: add badges --- .github/depandabot.yml | 6 + .github/workflows/go.yml | 27 +++ .gitignore | 1 + README.md | 9 + cic_net/cic_net.go | 48 +++++ cic_net/cic_net_test.go | 32 ++++ cic_net/cicnet.go | 32 ---- cic_net/erc20_demurrage_token.go | 102 +++++++++-- cic_net/erc20_demurrage_token_test.go | 251 ++++++++++++++++++++++++++ cic_net/erc20_token.go | 52 ++++++ cic_net/erc20_token_test.go | 116 ++++++++++++ cic_net/token_index_test.go | 83 +++++++++ cic_net/util.go | 50 +++++ 13 files changed, 761 insertions(+), 48 deletions(-) create mode 100644 .github/depandabot.yml create mode 100644 .github/workflows/go.yml create mode 100644 .gitignore create mode 100644 cic_net/cic_net.go create mode 100644 cic_net/cic_net_test.go delete mode 100644 cic_net/cicnet.go create mode 100644 cic_net/erc20_demurrage_token_test.go create mode 100644 cic_net/erc20_token.go create mode 100644 cic_net/erc20_token_test.go create mode 100644 cic_net/token_index_test.go create mode 100644 cic_net/util.go diff --git a/.github/depandabot.yml b/.github/depandabot.yml new file mode 100644 index 0000000..20e1ef1 --- /dev/null +++ b/.github/depandabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "gomod" + directory: "/" + schedule: + interval: "daily" \ No newline at end of file diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml new file mode 100644 index 0000000..aa75e95 --- /dev/null +++ b/.github/workflows/go.yml @@ -0,0 +1,27 @@ +name: Go + +on: + - push + +jobs: + test: + name: Test + runs-on: ubuntu-latest + environment: test + steps: + - uses: actions/setup-go@v3 + with: + go-version: 1.18 + - uses: actions/checkout@v2 + - name: go test + env: + TOKEN_INDEX: ${{ secrets.TOKEN_INDEX }} + RPC_PROVIDER: ${{ secrets.RPC_PROVIDER }} + PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }} + run: go test -v -covermode atomic -coverprofile=covprofile ./... + - name: install goveralls + run: go install github.com/mattn/goveralls@latest + - name: send coverage + env: + COVERALLS_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: goveralls -coverprofile=covprofile -service=github \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3b4b165 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +covprofile diff --git a/README.md b/README.md index 7ca3a4c..96fa363 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,11 @@ +[![Go Reference](https://pkg.go.dev/badge/github.com/grassrootseconomics/cic_go.svg)](https://pkg.go.dev/github.com/grassrootseconomics/cic_go) +[![Go](https://github.com/grassrootseconomics/cic_go/actions/workflows/go.yml/badge.svg)](https://github.com/grassrootseconomics/cic_go/actions/workflows/go.yml) +[![Coverage Status](https://coveralls.io/repos/github/grassrootseconomics/cic_go/badge.svg?branch=sohail/cic_net_updates)](https://coveralls.io/github/grassrootseconomics/cic_go?branch=sohail/cic_net_updates) + # cic_go Go modules to access various parts of the cic stack + +## Implemented + +- cic_net +- cic_meta \ No newline at end of file diff --git a/cic_net/cic_net.go b/cic_net/cic_net.go new file mode 100644 index 0000000..f20e9fb --- /dev/null +++ b/cic_net/cic_net.go @@ -0,0 +1,48 @@ +package cic_net + +import ( + "crypto/ecdsa" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/lmittmann/w3" + "math/big" +) + +const ( + kitabuMainnetChainId = 6060 +) + +type CicNet struct { + ethClient *w3.Client + tokenIndex common.Address + kitabuSigner types.Signer +} + +type WriteTx struct { + from common.Address + to common.Address + gasLimit uint64 + privateKey ecdsa.PrivateKey +} + +func NewCicNet(rpcEndpoint string, tokenIndex common.Address) (*CicNet, error) { + ethClient, err := w3.Dial(rpcEndpoint) + if err != nil { + return nil, err + } + + return &CicNet{ + ethClient: ethClient, + tokenIndex: tokenIndex, + kitabuSigner: types.NewEIP155Signer(big.NewInt(kitabuMainnetChainId)), + }, nil +} + +func (c *CicNet) Close() error { + err := c.ethClient.Close() + if err != nil { + return err + } + + return nil +} diff --git a/cic_net/cic_net_test.go b/cic_net/cic_net_test.go new file mode 100644 index 0000000..cc24650 --- /dev/null +++ b/cic_net/cic_net_test.go @@ -0,0 +1,32 @@ +package cic_net + +import ( + "github.com/lmittmann/w3" + "os" + "testing" +) + +type tConfig struct { + rpcProvider string + tokenIndex string + privateKey string +} + +var conf = &tConfig{ + rpcProvider: os.Getenv("RPC_PROVIDER"), + tokenIndex: os.Getenv("TOKEN_INDEX"), + privateKey: os.Getenv("PRIVATE_KEY"), +} + +func TestCicNet_Connect(t *testing.T) { + name := "Test RPC connection" + wantErr := false + + t.Run(name, func(t *testing.T) { + cicnet, _ := NewCicNet(conf.rpcProvider, w3.A(conf.tokenIndex)) + + if err := cicnet.Close(); (err != nil) != wantErr { + t.Errorf("EntryCount() error = %v, wantErr %v", err, wantErr) + } + }) +} diff --git a/cic_net/cicnet.go b/cic_net/cicnet.go deleted file mode 100644 index 61e8f11..0000000 --- a/cic_net/cicnet.go +++ /dev/null @@ -1,32 +0,0 @@ -package cic_net - -import ( - "github.com/ethereum/go-ethereum/common" - "github.com/lmittmann/w3" -) - -type CicNet struct { - ethClient *w3.Client - tokenIndex common.Address -} - -func NewCicNet(rpcEndpoint string, tokenIndex common.Address) (*CicNet, error) { - ethClient, err := w3.Dial(rpcEndpoint) - if err != nil { - return nil, err - } - - return &CicNet{ - ethClient: ethClient, - tokenIndex: tokenIndex, - }, nil -} - -func (c *CicNet) Close() error { - err := c.ethClient.Close() - if err != nil { - return err - } - - return nil -} diff --git a/cic_net/erc20_demurrage_token.go b/cic_net/erc20_demurrage_token.go index 7ccbca3..3240ebc 100644 --- a/cic_net/erc20_demurrage_token.go +++ b/cic_net/erc20_demurrage_token.go @@ -8,32 +8,102 @@ import ( "math/big" ) -type DemurrageToken struct { - Name string - Symbol string - Decimals big.Int +type DemurrageTokenInfo struct { + DemurrageAmount big.Int + DemurrageTimestamp big.Int + MinimumParticipantSpend big.Int + ResolutionFactor big.Int + PeriodStart big.Int + PeriodDuration big.Int + TaxLevel big.Int + ActualPeriod big.Int + RedistributionCount big.Int + IsDemurrageToken bool } -func (c *CicNet) TokenInfo(ctx context.Context, tokenAddress common.Address) (DemurrageToken, error) { +func (c *CicNet) DemurrageTokenInfo(ctx context.Context, tokenAddress common.Address) (DemurrageTokenInfo, error) { var ( - tokenName string - tokenSymbol string - tokenDecimals big.Int + demurrageAmount big.Int + demurrageTimestamp big.Int + minimumParticipantSpend big.Int + resolutionFactor big.Int + periodStart big.Int + periodDuration big.Int + taxLevel big.Int + actualPeriod big.Int + redistributionCount big.Int ) err := c.ethClient.CallCtx( ctx, - eth.CallFunc(w3.MustNewFunc("name()", "string"), tokenAddress).Returns(&tokenName), - eth.CallFunc(w3.MustNewFunc("symbol()", "string"), tokenAddress).Returns(&tokenSymbol), - eth.CallFunc(w3.MustNewFunc("decimals()", "uint256"), tokenAddress).Returns(&tokenDecimals), + eth.CallFunc(w3.MustNewFunc("demurrageAmount()", "uint128"), tokenAddress).Returns(&demurrageAmount), + eth.CallFunc(w3.MustNewFunc("demurrageTimestamp()", "uint256"), tokenAddress).Returns(&demurrageTimestamp), + eth.CallFunc(w3.MustNewFunc("minimumParticipantSpend()", "uint256"), tokenAddress).Returns(&minimumParticipantSpend), + eth.CallFunc(w3.MustNewFunc("resolutionFactor()", "uint256"), tokenAddress).Returns(&resolutionFactor), + eth.CallFunc(w3.MustNewFunc("periodStart()", "uint256"), tokenAddress).Returns(&periodStart), + eth.CallFunc(w3.MustNewFunc("periodDuration()", "uint256"), tokenAddress).Returns(&periodDuration), + eth.CallFunc(w3.MustNewFunc("taxLevel()", "uint256"), tokenAddress).Returns(&taxLevel), + eth.CallFunc(w3.MustNewFunc("actualPeriod()", "uint256"), tokenAddress).Returns(&actualPeriod), + eth.CallFunc(w3.MustNewFunc("redistributionCount()", "uint256"), tokenAddress).Returns(&redistributionCount), ) + if err != nil { - return DemurrageToken{}, err + return DemurrageTokenInfo{}, err } - return DemurrageToken{ - Name: tokenName, - Symbol: tokenSymbol, - Decimals: tokenDecimals, + return DemurrageTokenInfo{ + DemurrageAmount: demurrageAmount, + DemurrageTimestamp: demurrageTimestamp, + MinimumParticipantSpend: minimumParticipantSpend, + ResolutionFactor: resolutionFactor, + PeriodStart: periodStart, + PeriodDuration: periodDuration, + TaxLevel: taxLevel, + ActualPeriod: actualPeriod, + RedistributionCount: redistributionCount, }, nil } + +func (c *CicNet) BaseBalanceOf(ctx context.Context, tokenAddress common.Address, accountAddress common.Address) (big.Int, error) { + var balance big.Int + + err := c.ethClient.CallCtx( + ctx, + eth.CallFunc(w3.MustNewFunc("baseBalanceOf(address _account)", "uint256"), tokenAddress, accountAddress).Returns(&balance), + ) + if err != nil { + return big.Int{}, err + } + + return balance, nil +} + +func (c *CicNet) ChangePeriod(ctx context.Context, txData WriteTx) (common.Hash, error) { + sig := w3.MustNewFunc("changePeriod()", "bool") + input, err := sig.EncodeArgs() + if err != nil { + return [32]byte{}, err + } + + txHash, err := c.signAndCall(ctx, input, txData) + if err != nil { + return [32]byte{}, err + } + + return txHash, nil +} + +func (c *CicNet) ApplyDemurrageLimited(ctx context.Context, rounds int64, txData WriteTx) (common.Hash, error) { + sig := w3.MustNewFunc("applyDemurrageLimited(uint256 _rounds)", "bool") + input, err := sig.EncodeArgs(big.NewInt(rounds)) + if err != nil { + return [32]byte{}, err + } + + txHash, err := c.signAndCall(ctx, input, txData) + if err != nil { + return [32]byte{}, err + } + + return txHash, nil +} diff --git a/cic_net/erc20_demurrage_token_test.go b/cic_net/erc20_demurrage_token_test.go new file mode 100644 index 0000000..366f23b --- /dev/null +++ b/cic_net/erc20_demurrage_token_test.go @@ -0,0 +1,251 @@ +package cic_net + +import ( + "context" + "crypto/ecdsa" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/lmittmann/w3" + "math/big" + "testing" +) + +func TestCicNet_DemurrageToken_DemurrageTokeInfo(t *testing.T) { + type args struct { + contractAddress common.Address + } + + tests := []struct { + name string + args args + wantErr bool + isDemurrage bool + }{ + { + name: "Demurrage token at kitabu sarafu", + args: args{ + contractAddress: w3.A("0xaB89822F31c2092861F713F6F34bd6877a8C1878"), + }, + wantErr: false, + }, + { + name: "Giftable token at Muthaa", + args: args{ + contractAddress: w3.A("0x3dad47e5EF13661bbD15aa74132E91a9aBCFDe44"), + }, + wantErr: true, + }, + { + name: "Dead address", + args: args{ + contractAddress: w3.A("0x000000000000000000000000000000000000dEaD"), + }, + wantErr: true, + }, + } + + for _, testcase := range tests { + tt := testcase + + t.Run(tt.name, func(t *testing.T) { + cicnet, err := NewCicNet(conf.rpcProvider, w3.A(conf.tokenIndex)) + + if err != nil { + t.Fatalf("NewCicNet error = %v", err) + } + + got, err := cicnet.DemurrageTokenInfo(context.Background(), tt.args.contractAddress) + + if (err != nil) != tt.wantErr { + t.Errorf("DemurrageTokenInfo() error = %v, wantErr %v", err, tt.wantErr) + } + + if !tt.wantErr { + if got.DemurrageAmount.Cmp(big.NewInt(0)) < 1 { + t.Fatalf("DemurrageAmount = %v, want %d atleast", got, 1) + } + } + }) + } +} + +func TestCicNet_DemurrageToken_BaseBalanceOf(t *testing.T) { + type args struct { + contractAddress common.Address + accountAddress common.Address + } + + tests := []struct { + name string + args args + wantErr bool + balanceGte big.Int + }{ + { + name: "Sarafu sink balance", + args: args{ + contractAddress: w3.A("0xaB89822F31c2092861F713F6F34bd6877a8C1878"), + accountAddress: w3.A("0xBBb4a93c8dCd82465B73A143f00FeD4AF7492a27"), + }, + wantErr: false, + balanceGte: *big.NewInt(1), + }, + { + name: "Dead address balance", + args: args{ + contractAddress: w3.A("0xaB89822F31c2092861F713F6F34bd6877a8C1878"), + accountAddress: w3.A("0x000000000000000000000000000000000000dEaD"), + }, + wantErr: false, + balanceGte: *big.NewInt(0), + }, + { + name: "Giftable token at Muthaa", + args: args{ + contractAddress: w3.A("0x3dad47e5EF13661bbD15aa74132E91a9aBCFDe44"), + accountAddress: w3.A("0xBBb4a93c8dCd82465B73A143f00FeD4AF7492a27"), + }, + wantErr: true, + }, + } + + for _, testcase := range tests { + tt := testcase + + t.Run(tt.name, func(t *testing.T) { + cicnet, err := NewCicNet(conf.rpcProvider, w3.A(conf.tokenIndex)) + + if err != nil { + t.Fatalf("NewCicNet error = %v", err) + } + + got, err := cicnet.BaseBalanceOf(context.Background(), tt.args.contractAddress, tt.args.accountAddress) + + if (err != nil) != tt.wantErr { + t.Errorf("BaseBalanceOf() error = %v, wantErr %v", err, tt.wantErr) + } + + if !tt.wantErr { + if got.Cmp(&tt.balanceGte) < 0 { + t.Fatalf("Token = %v, want %d", got, tt.balanceGte.Int64()) + } + } + }) + } +} + +func TestCicNet_DemurrageToken_ChangePeriod(t *testing.T) { + type args struct { + writeTx WriteTx + } + + // Bootstrap signer + privateKey, err := crypto.HexToECDSA(conf.privateKey) + if err != nil { + t.Fatalf("ECDSA error = %v", err) + } + publicKey := privateKey.Public() + publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) + if !ok { + t.Fatalf("ECDSA error = %v", err) + } + fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA) + + tests := []struct { + name string + args args + wantErr bool + balanceGte big.Int + }{ + { + name: "ChangePeriod for Sarafu", + args: args{ + writeTx: WriteTx{ + from: fromAddress, + to: w3.A("0xaB89822F31c2092861F713F6F34bd6877a8C1878"), + gasLimit: 12000000, + privateKey: *privateKey, + }, + }, + wantErr: false, + }, + } + + for _, testcase := range tests { + tt := testcase + + t.Run(tt.name, func(t *testing.T) { + cicnet, err := NewCicNet(conf.rpcProvider, w3.A(conf.tokenIndex)) + + if err != nil { + t.Fatalf("NewCicNet error = %v", err) + } + + tx, err := cicnet.ChangePeriod(context.Background(), tt.args.writeTx) + t.Logf("ChangePeriod tx_hash %s", tx.String()) + + if (err != nil) != tt.wantErr { + t.Errorf("ChangePeriod() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestCicNet_DemurrageToken_ApplyDemurrageLimited(t *testing.T) { + type args struct { + rounds int64 + writeTx WriteTx + } + + // Bootstrap signer + privateKey, err := crypto.HexToECDSA(conf.privateKey) + if err != nil { + t.Fatalf("ECDSA error = %v", err) + } + publicKey := privateKey.Public() + publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) + if !ok { + t.Fatalf("ECDSA error = %v", err) + } + fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA) + + tests := []struct { + name string + args args + wantErr bool + balanceGte big.Int + }{ + { + name: "ChangePeriod for Sarafu", + args: args{ + rounds: 1000, + writeTx: WriteTx{ + from: fromAddress, + to: w3.A("0xaB89822F31c2092861F713F6F34bd6877a8C1878"), + gasLimit: 12000000, + privateKey: *privateKey, + }, + }, + wantErr: false, + }, + } + + for _, testcase := range tests { + tt := testcase + + t.Run(tt.name, func(t *testing.T) { + cicnet, err := NewCicNet(conf.rpcProvider, w3.A(conf.tokenIndex)) + + if err != nil { + t.Fatalf("NewCicNet error = %v", err) + } + + tx, err := cicnet.ApplyDemurrageLimited(context.Background(), tt.args.rounds, tt.args.writeTx) + t.Logf("ApplyDemurrageLimited tx_hash %s", tx.String()) + + if (err != nil) != tt.wantErr { + t.Errorf("ApplyDemurrageLimited() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/cic_net/erc20_token.go b/cic_net/erc20_token.go new file mode 100644 index 0000000..74d4d8d --- /dev/null +++ b/cic_net/erc20_token.go @@ -0,0 +1,52 @@ +package cic_net + +import ( + "context" + "github.com/ethereum/go-ethereum/common" + "github.com/lmittmann/w3" + "github.com/lmittmann/w3/module/eth" + "math/big" +) + +type ERC20Token struct { + Name string + Symbol string + Decimals big.Int +} + +func (c *CicNet) ERC20TokenInfo(ctx context.Context, tokenAddress common.Address) (ERC20Token, error) { + var ( + tokenName string + tokenSymbol string + tokenDecimals big.Int + ) + + err := c.ethClient.CallCtx( + ctx, + eth.CallFunc(w3.MustNewFunc("name()", "string"), tokenAddress).Returns(&tokenName), + eth.CallFunc(w3.MustNewFunc("symbol()", "string"), tokenAddress).Returns(&tokenSymbol), + eth.CallFunc(w3.MustNewFunc("decimals()", "uint256"), tokenAddress).Returns(&tokenDecimals), + ) + if err != nil { + return ERC20Token{}, err + } + + return ERC20Token{ + Name: tokenName, + Symbol: tokenSymbol, + Decimals: tokenDecimals, + }, nil +} +func (c *CicNet) BalanceOf(ctx context.Context, tokenAddress common.Address, accountAddress common.Address) (big.Int, error) { + var balance big.Int + + err := c.ethClient.CallCtx( + ctx, + eth.CallFunc(w3.MustNewFunc("balanceOf(address _account)", "uint256"), tokenAddress, accountAddress).Returns(&balance), + ) + if err != nil { + return big.Int{}, err + } + + return balance, nil +} diff --git a/cic_net/erc20_token_test.go b/cic_net/erc20_token_test.go new file mode 100644 index 0000000..9208fb8 --- /dev/null +++ b/cic_net/erc20_token_test.go @@ -0,0 +1,116 @@ +package cic_net + +import ( + "context" + "github.com/ethereum/go-ethereum/common" + "github.com/lmittmann/w3" + "math/big" + "testing" +) + +func TestCicNet_ERC20Token_ERC20TokenInfo(t *testing.T) { + type args struct { + contractAddress common.Address + } + + tests := []struct { + name string + args args + wantErr bool + symbol string + }{ + { + name: "Token at kitabu sarafu", + args: args{ + contractAddress: w3.A("0xaB89822F31c2092861F713F6F34bd6877a8C1878"), + }, + wantErr: false, + symbol: "SRF", + }, + { + name: "Token at zero address", + args: args{ + contractAddress: w3.A("0x0000000000000000000000000000000000000000"), + }, + wantErr: true, + symbol: "", + }, + } + + for _, testcase := range tests { + tt := testcase + + t.Run(tt.name, func(t *testing.T) { + cicnet, err := NewCicNet(conf.rpcProvider, w3.A(conf.tokenIndex)) + + if err != nil { + t.Fatalf("NewCicNet error = %v", err) + } + + got, err := cicnet.ERC20TokenInfo(context.Background(), tt.args.contractAddress) + + if (err != nil) != tt.wantErr { + t.Errorf("ERC20TokenInfo() error = %v, wantErr %v", err, tt.wantErr) + } + + if got.Symbol != tt.symbol { + t.Fatalf("Token = %v, want %v", got, tt.symbol) + } + }) + } +} + +func TestCicNet_ERC20Token_BalanceOf(t *testing.T) { + type args struct { + contractAddress common.Address + accountAddress common.Address + } + + tests := []struct { + name string + args args + wantErr bool + balanceGte big.Int + }{ + { + name: "Sarafu sink balance", + args: args{ + contractAddress: w3.A("0xaB89822F31c2092861F713F6F34bd6877a8C1878"), + accountAddress: w3.A("0xBBb4a93c8dCd82465B73A143f00FeD4AF7492a27"), + }, + wantErr: false, + balanceGte: *big.NewInt(1), + }, + { + name: "Dead address balance", + args: args{ + contractAddress: w3.A("0xaB89822F31c2092861F713F6F34bd6877a8C1878"), + accountAddress: w3.A("0x000000000000000000000000000000000000dEaD"), + }, + wantErr: false, + balanceGte: *big.NewInt(0), + }, + } + + for _, testcase := range tests { + tt := testcase + + t.Run(tt.name, func(t *testing.T) { + cicnet, err := NewCicNet(conf.rpcProvider, w3.A(conf.tokenIndex)) + + if err != nil { + t.Fatalf("NewCicNet error = %v", err) + } + + got, err := cicnet.BalanceOf(context.Background(), tt.args.contractAddress, tt.args.accountAddress) + + if (err != nil) != tt.wantErr { + t.Errorf("BalanceOf() error = %v, wantErr %v", err, tt.wantErr) + } + + if got.Cmp(&tt.balanceGte) < 0 { + t.Fatalf("Token = %v, want %d", got, tt.balanceGte.Int64()) + } + }) + } +} diff --git a/cic_net/token_index_test.go b/cic_net/token_index_test.go new file mode 100644 index 0000000..48aa3f7 --- /dev/null +++ b/cic_net/token_index_test.go @@ -0,0 +1,83 @@ +package cic_net + +import ( + "context" + "github.com/lmittmann/w3" + "math/big" + "testing" +) + +func TestCicNet_TokenIndex_EntryCount(t *testing.T) { + name := "Entry count" + wantErr := false + + t.Run(name, func(t *testing.T) { + tokenIndex, err := NewCicNet(conf.rpcProvider, w3.A(conf.tokenIndex)) + + if err != nil { + t.Fatalf("NewCicNet error = %v", err) + } + + got, err := tokenIndex.EntryCount(context.Background()) + + if (err != nil) != wantErr { + t.Errorf("EntryCount() error = %v, wantErr %v", err, wantErr) + } + + if got.Cmp(big.NewInt(0)) < 1 { + t.Fatalf("EntryCount() = %v, want %v", got, 1) + } + }) +} + +func TestCicNet_TokenIndex_AddressAtIndex(t *testing.T) { + type args struct { + index *big.Int + } + + tests := []struct { + name string + args args + wantErr bool + address string + }{ + { + name: "Address at index 0", + args: args{ + index: big.NewInt(0), + }, + wantErr: false, + address: "0xaB89822F31c2092861F713F6F34bd6877a8C1878", + }, + { + name: "Address at index 999", + args: args{ + index: big.NewInt(999), + }, + wantErr: true, + address: "", + }, + } + + for _, testcase := range tests { + tt := testcase + + t.Run(tt.name, func(t *testing.T) { + tokenIndex, err := NewCicNet(conf.rpcProvider, w3.A(conf.tokenIndex)) + + if err != nil { + t.Fatalf("NewCicNet error = %v", err) + } + + got, err := tokenIndex.AddressAtIndex(context.Background(), tt.args.index) + + if (err != nil) != tt.wantErr { + t.Errorf("AddressAtIndex() error = %v, wantErr %v", err, tt.wantErr) + } + + if got != tt.address { + t.Fatalf("AddressAtIndex = %v, want %v", got, tt.address) + } + }) + } +} diff --git a/cic_net/util.go b/cic_net/util.go new file mode 100644 index 0000000..23ec89e --- /dev/null +++ b/cic_net/util.go @@ -0,0 +1,50 @@ +package cic_net + +import ( + "context" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/lmittmann/w3/module/eth" + "math/big" +) + +func (c *CicNet) latestNonce(ctx context.Context, address common.Address) (uint64, error) { + var nonce uint64 + + err := c.ethClient.CallCtx( + ctx, + eth.Nonce(address, nil).Returns(&nonce), + ) + if err != nil { + return 0, err + } + + return nonce, nil +} + +func (c *CicNet) signAndCall(ctx context.Context, input []byte, txData WriteTx) (common.Hash, error) { + var txHash common.Hash + + nonce, err := c.latestNonce(ctx, txData.from) + if err != nil { + return [32]byte{}, err + } + + tx, err := types.SignNewTx(&txData.privateKey, c.kitabuSigner, &types.LegacyTx{ + To: &txData.to, + Nonce: nonce, + Data: input, + Gas: txData.gasLimit, + GasPrice: big.NewInt(1), + }) + + err = c.ethClient.CallCtx( + ctx, + eth.SendTransaction(tx).Returns(&txHash), + ) + if err != nil { + return [32]byte{}, err + } + + return txHash, nil +}