Skip to content

Commit 7312744

Browse files
feat: add divvi submit referral tag
1 parent e83b676 commit 7312744

4 files changed

Lines changed: 100 additions & 6 deletions

File tree

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ Features:
1616
* Grassroots Economics [address](https://software.grassecon.org/addresses) loaders
1717
* Dump reverted tx reason
1818
* Batch balances scanner
19-
* Index iterator
19+
* Index scanner
20+
* Divvi support
2021

2122
## Installation
2223

divvi.go

Lines changed: 89 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,112 @@
11
package ethutils
22

33
import (
4+
"bytes"
5+
"context"
46
"encoding/hex"
7+
"encoding/json"
8+
"errors"
9+
"fmt"
10+
"io"
511
"math/big"
12+
"net/http"
613

714
"github.com/ethereum/go-ethereum/common"
815
)
916

17+
type SubmitBody struct {
18+
TxHash string `json:"txHash"`
19+
ChainID int64 `json:"chainId"`
20+
}
21+
1022
const (
1123
divviMagicPrefix = "6decb85d"
1224
knownEmptyAddressArrayEncoding = "00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000"
25+
26+
divviAPIEndpoint = "https://api.divvi.xyz/submitReferral"
27+
userAgent = "ethutils/v1.x.x"
28+
contentType = "application/json"
1329
)
1430

15-
var referalTagFormat1Byte = []byte{0x01}
31+
var (
32+
referalTagFormat1Byte = []byte{0x01}
33+
34+
ErrDivviClientNotInitialized = errors.New("divvi client not initialized")
35+
)
1636

1737
func (p *Provider) GetReferalTag(user common.Address) []byte {
18-
encodedBytes := concatBytes(encodeAddress(user), encodeAddress(p.DivviConsumerAddress), encodeAddressArrayEmpty())
38+
encodedBytes := ConcatBytes(encodeAddress(user), encodeAddress(p.DivviConsumerAddress), encodeAddressArrayEmpty())
1939
payloadLen := []byte{byte(len(encodedBytes) >> 8), byte(len(encodedBytes))}
2040
magicPrefix, _ := hex.DecodeString(divviMagicPrefix)
21-
payload := concatBytes(magicPrefix, referalTagFormat1Byte, payloadLen, encodedBytes)
41+
payload := ConcatBytes(magicPrefix, referalTagFormat1Byte, payloadLen, encodedBytes)
2242
return payload
2343
}
2444

45+
func (p *Provider) SubmitReferral(ctx context.Context, txHash common.Hash) error {
46+
if p.DivviClient == nil {
47+
return ErrDivviClientNotInitialized
48+
}
49+
body := SubmitBody{
50+
TxHash: txHash.Hex(),
51+
ChainID: CeloMainnet,
52+
}
53+
54+
b, err := json.Marshal(&body)
55+
if err != nil {
56+
return err
57+
}
58+
59+
resp, err := p.requestWithCtx(ctx, http.MethodPost, divviAPIEndpoint, bytes.NewBuffer(b))
60+
if err != nil {
61+
return err
62+
}
63+
64+
if err := parseResponse(resp); err != nil {
65+
return err
66+
}
67+
68+
return nil
69+
}
70+
71+
func (p *Provider) requestWithCtx(ctx context.Context, method string, url string, body io.Reader) (*http.Response, error) {
72+
req, err := http.NewRequestWithContext(ctx, method, url, body)
73+
if err != nil {
74+
return nil, err
75+
}
76+
return p.do(req)
77+
}
78+
79+
func (p *Provider) do(req *http.Request) (*http.Response, error) {
80+
builtRequest, err := setHeaders(req)
81+
if err != nil {
82+
return nil, err
83+
}
84+
85+
return p.DivviClient.Do(builtRequest)
86+
}
87+
88+
func setHeaders(req *http.Request) (*http.Request, error) {
89+
req.Header.Set("User-Agent", userAgent)
90+
req.Header.Set("Accept", contentType)
91+
req.Header.Set("Content-Type", contentType)
92+
93+
return req, nil
94+
}
95+
96+
func parseResponse(resp *http.Response) error {
97+
defer resp.Body.Close()
98+
99+
if resp.StatusCode >= http.StatusBadRequest {
100+
b, err := io.ReadAll(resp.Body)
101+
if err != nil {
102+
return fmt.Errorf("divvi api error: status=%s", resp.Status)
103+
}
104+
return fmt.Errorf("divvi api error: status=%s body=%s", resp.Status, string(b))
105+
}
106+
107+
return nil
108+
}
109+
25110
func encodeAddress(address common.Address) []byte {
26111
return common.LeftPadBytes(address.Bytes(), 32)
27112
}
@@ -51,7 +136,7 @@ func encodeAddressArrayEmpty() []byte {
51136
return emptyBytes
52137
}
53138

54-
func concatBytes(slices ...[]byte) []byte {
139+
func ConcatBytes(slices ...[]byte) []byte {
55140
var result []byte
56141
for _, slice := range slices {
57142
result = append(result, slice...)

provider.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package ethutils
22

33
import (
44
"math/big"
5+
"net/http"
6+
"time"
57

68
"github.com/ethereum/go-ethereum/common"
79
"github.com/ethereum/go-ethereum/core/types"
@@ -16,6 +18,7 @@ type (
1618
Signer types.Signer
1719
BalanceScannerContractAddress string
1820
DivviConsumerAddress common.Address
21+
DivviClient *http.Client
1922
}
2023
)
2124

@@ -40,6 +43,9 @@ func WithBalanceScannerAddress(address string) Option {
4043
func WithDivviConsumerAddress(address string) Option {
4144
return func(p *Provider) {
4245
p.DivviConsumerAddress = w3.A(address)
46+
p.DivviClient = &http.Client{
47+
Timeout: time.Second * 10,
48+
}
4349
}
4450
}
4551

transaction.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ type (
2020

2121
GasTransferTxOpts struct {
2222
To common.Address
23+
InputData []byte
2324
Value *big.Int
2425
GasFeeCap *big.Int
2526
GasTipCap *big.Int
@@ -54,9 +55,10 @@ func (p *Provider) SignContractExecutionTx(privateKey *ecdsa.PrivateKey, txData
5455
func (p *Provider) SignGasTransferTx(privateKey *ecdsa.PrivateKey, txData GasTransferTxOpts) (*types.Transaction, error) {
5556
tx, err := types.SignNewTx(privateKey, p.Signer, &types.DynamicFeeTx{
5657
Value: txData.Value,
58+
Data: txData.InputData,
5759
To: &txData.To,
5860
Nonce: txData.Nonce,
59-
Gas: 21000,
61+
Gas: 25000,
6062
GasFeeCap: txData.GasFeeCap,
6163
GasTipCap: txData.GasTipCap,
6264
})

0 commit comments

Comments
 (0)