-
Notifications
You must be signed in to change notification settings - Fork 1
/
main.go
221 lines (190 loc) · 7.12 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
package main
import (
"context"
"fmt"
"log"
"math/big"
"os"
"strings"
"time"
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ssm"
ethereum "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/contracts/chequebook"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
"gitlab.com/deqode/tokenholder-management-lambda/blockchain/contracts"
"gitlab.com/deqode/tokenholder-management-lambda/config"
"gitlab.com/deqode/tokenholder-management-lambda/types"
)
var (
int2 = big.NewInt(2)
int3 = big.NewInt(3)
gasLimit = int64(21000)
)
func init() {
path, found := os.LookupEnv("SSM_PS_PATH")
if found {
session, err := session.NewSession(aws.NewConfig())
if err != nil {
log.Printf("error: failed to create new session %s\n", err)
os.Exit(1)
}
service := ssm.New(session)
withDecryption := true
request := ssm.GetParametersByPathInput{Path: &path, WithDecryption: &withDecryption}
response, err := service.GetParametersByPath(&request)
if err != nil {
log.Printf("error: failed to get parameters, %s\n", err)
os.Exit(1)
}
for _, parameter := range response.Parameters {
paramName := strings.ToUpper(strings.TrimPrefix(*parameter.Name, path))
os.Setenv(paramName, *parameter.Value)
log.Printf("set env variable: %s\n", paramName)
}
}
}
// HandleEvent will handle the event that triggered Lambda function
func HandleEvent(ctx context.Context, event events.CloudWatchEvent) (string, error) {
err := config.Parse()
if err != nil {
return formatError("Unable to parse environment variables with error : %s", err)
}
// Update the ethereum json RPC url as per the enviroment
ethClient, err := ethclient.Dial(config.EthereumJSONRPCURL)
if err != nil {
return formatError("Failed to call ethereum client with error : %s", err)
}
gasPrice, err := ethClient.SuggestGasPrice(ctx)
if err != nil {
return formatError("Failed to get gas price from ethereum client with error : %s", err)
}
// deqode vault private key
deqodeVaultPrivateKey, err := crypto.HexToECDSA(config.deqodeVaultPrivateKey)
if err != nil {
return formatError("Failed to parse ecdsa private key from deqode vault private key sting with error : %s", err)
}
// token holder processing key
tokenHolderProcessingKey, err := crypto.HexToECDSA(config.TokenHolderProcessingKey)
if err != nil {
return formatError("Failed to parse ecdsa private key from token holder processing key string with error : %s", err)
}
// deqode address (to which 2/3rd of revenue will be transfered)
deqodeAddress := common.HexToAddress(config.deqodeAddress)
// Token holder contract address
tokenHolderContractAddress := common.HexToAddress(config.TokenHolderContractAddress)
// deqode vault address
deqodeVaultAddress := crypto.PubkeyToAddress(deqodeVaultPrivateKey.PublicKey)
vaultBalance, err := ethClient.BalanceAt(ctx, deqodeVaultAddress, nil)
if err != nil {
return formatError("Failed to get balance for vault with error : %s", err)
}
// Some balance is left to cover txns fees (gaslimit* 2 * gasPrice)
vaultBalance.Sub(vaultBalance, new(big.Int).Mul(big.NewInt(gasLimit*2), gasPrice))
// Check if balance is positive after fees is subtracted
if vaultBalance.Cmp(big.NewInt(0)) != 1 {
return formatError("Not enough balance in vault to cover txn fees")
}
// Set auth key, gasPrice, gasLimit
vaultAuth := bind.NewKeyedTransactor(deqodeVaultPrivateKey)
vaultAuth.GasPrice = gasPrice
vaultAuth.GasLimit = uint64(gasLimit)
// Transferfing 1/3 vault balance to token holder contract
vaultAuth.Value = new(big.Int).Div(vaultBalance, int3)
txn, err := ethereum.Transferer{ContractTransactor: ethClient}.Transfer(vaultAuth, tokenHolderContractAddress, nil)
if err != nil {
return formatError("Failed to transfer vault balance to token holder contract with error : %s", err)
}
// waiting for token holder fund transfer txn
err = waitForTx(ctx, ethClient, txn.Hash())
if err != nil {
return formatError("Fund transfer txn with hash %s failed with error : %s", txn.Hash().Hex(), err)
}
// Transferfing 2/3 vault balance to deqode address
vaultAuth.Value = new(big.Int).Mul(new(big.Int).Div(vaultBalance, int3), int2)
txn, err = ethereum.Transferer{ContractTransactor: ethClient}.Transfer(vaultAuth, deqodeAddress, nil)
if err != nil {
return formatError("Failed to transfer vault balance to deqode address with error : %s", err)
}
// waiting for deqode address fund transfer txn
err = waitForTx(ctx, ethClient, txn.Hash())
if err != nil {
return formatError("Fund transfer txn with hash %s failed with error : %s", txn.Hash().Hex(), err)
}
// Creating instance for token holder program contract
tokenHolderContract, err := contracts.NewdeqodeTokenHolderProgramContract(tokenHolderContractAddress, ethClient)
if err != nil {
return formatError("Failed to load token holder contract for address %s with error : %s", tokenHolderContractAddress.String(), err)
}
tokenHolderAuth := bind.NewKeyedTransactor(tokenHolderProcessingKey)
// Calling sell vouchers of token holder program contract
txn, err = tokenHolderContract.SellVouchers(tokenHolderAuth)
if err != nil {
return formatError("Failed to call sell vouchers of token holder program contract with error : %s", err)
}
// waiting for token holder sell voucher txn
err = waitForTx(ctx, ethClient, txn.Hash())
if err != nil {
return formatError("Sell vouchers txn with hash %s failed with error : %s", txn.Hash().Hex(), err)
}
// Calling buy vouchers of token holder program contract
txn, err = tokenHolderContract.BuyVouchers(tokenHolderAuth)
if err != nil {
return formatError("Failed to call buy vouchers of token holder program contract with error : %s", err)
}
// waiting for token holder buy voucher txn
err = waitForTx(ctx, ethClient, txn.Hash())
if err != nil {
return formatError("Buy vouchers txn with hash %s failed with error : %s", txn.Hash().Hex(), err)
}
return fmt.Sprintf("Token holder event successfully called at %s!", event.Time), nil
}
func main() {
lambda.Start(HandleEvent)
}
func waitForTx(ctx context.Context, backend chequebook.Backend, txHash common.Hash) error {
log.Printf("Waiting for transaction: 0x%x", txHash)
type commiter interface {
Commit()
}
if sim, ok := backend.(commiter); ok {
sim.Commit()
tr, err := backend.TransactionReceipt(ctx, txHash)
if err != nil {
return err
}
if tr.Status != types.ReceiptStatusSuccessful {
return fmt.Errorf("tx failed: %+v", tr)
}
return nil
}
for {
select {
case <-ctx.Done():
return ctx.Err()
case <-time.After(4 * time.Second):
}
tr, err := backend.TransactionReceipt(ctx, txHash)
if err != nil {
if err == ethereum.NotFound {
continue
} else {
return err
}
} else {
if tr.Status != types.ReceiptStatusSuccessful {
return fmt.Errorf("tx failed: %+v", tr)
}
return nil
}
}
}
func formatError(format string, a ...interface{}) (string, error) {
return "", fmt.Errorf(format, a...)
}