Skip to content

Commit

Permalink
Send approve transaction on each deposit (0xPolygon#1288)
Browse files Browse the repository at this point in the history
* Send approve transaction on each deposit

* Try to set allowance bar slightly higher

* Approve and mint aggregated amount

* Fix defaultFundAmount

* Check if valid fund amount provided
  • Loading branch information
Stefan-Ethernal authored Mar 14, 2023
1 parent 2de54b2 commit 6fcaa2e
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 69 deletions.
114 changes: 86 additions & 28 deletions command/bridge/deposit/deposit_erc20.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,55 +153,91 @@ func runCommand(cmd *cobra.Command, _ []string) {
depositorKey = rootchainKey
}

depositorAddr := depositorKey.Address()

txRelayer, err := txrelayer.NewTxRelayer(txrelayer.WithIPAddress(dp.jsonRPCAddress))
if err != nil {
outputter.SetError(fmt.Errorf("failed to initialize rootchain tx relayer: %w", err))

return
}

amounts := make([]*big.Int, len(dp.Amounts))
aggregateAmount := new(big.Int)

for i, amountRaw := range dp.Amounts {
amountRaw := amountRaw

amount, err := types.ParseUint256orHex(&amountRaw)
if err != nil {
outputter.SetError(fmt.Errorf("failed to decode provided amount %s: %w", amount, err))

return
}

amounts[i] = amount
aggregateAmount.Add(aggregateAmount, amount)
}

if dp.testMode {
// mint tokens to depositor, so he is able to send them
mintTxn, err := createMintTxn(types.Address(depositorAddr), types.Address(depositorAddr), aggregateAmount)
if err != nil {
outputter.SetError(fmt.Errorf("mint transaction creation failed: %w", err))

return
}

receipt, err := txRelayer.SendTransaction(mintTxn, depositorKey)
if err != nil {
outputter.SetError(fmt.Errorf("failed to send mint transaction to depositor %s", depositorAddr))

return
}

if receipt.Status == uint64(types.ReceiptFailed) {
outputter.SetError(fmt.Errorf("failed to mint tokens to depositor %s", depositorAddr))

return
}
}

// approve root erc20 predicate
approveTxn, err := createApproveERC20PredicateTxn(aggregateAmount,
types.StringToAddress(dp.rootPredicateAddr),
types.StringToAddress(dp.rootTokenAddr))

receipt, err := txRelayer.SendTransaction(approveTxn, depositorKey)
if err != nil {
outputter.SetError(fmt.Errorf("failed to send root erc20 approve transaction"))

return
}

if receipt.Status == uint64(types.ReceiptFailed) {
outputter.SetError(fmt.Errorf("failed to approve root erc20 predicate"))

return
}

g, ctx := errgroup.WithContext(cmd.Context())

for i := range dp.Receivers {
receiver := dp.Receivers[i]
amount := dp.Amounts[i]
amount := amounts[i]

g.Go(func() error {
select {
case <-ctx.Done():
return ctx.Err()
default:
amountBig, err := types.ParseUint256orHex(&amount)
if err != nil {
return fmt.Errorf("failed to decode provided deposit amount %s: %w", amount, err)
}

if dp.testMode {
// mint tokens to the depositor, so he is able to deposit them
// Note: this works only if using test account on the rootchain,
// because it is expected that it is the one which deploys rootchain smart contracts as well
txn, err := createMintTxn(types.Address(depositorKey.Address()), types.Address(depositorKey.Address()), amountBig)
if err != nil {
return fmt.Errorf("mint transaction creation failed: %w", err)
}

receipt, err := txRelayer.SendTransaction(txn, depositorKey)
if err != nil {
return fmt.Errorf("failed to send mint transaction to depositor %s", depositorKey.Address())
}

if receipt.Status == uint64(types.ReceiptFailed) {
return fmt.Errorf("failed to mint tokens to depositor %s", depositorKey.Address())
}
}

// deposit tokens
txn, err := createDepositTxn(types.Address(depositorKey.Address()), types.StringToAddress(receiver), amountBig)
depositTxn, err := createDepositTxn(types.Address(depositorAddr), types.StringToAddress(receiver), amount)
if err != nil {
return fmt.Errorf("failed to create tx input: %w", err)
}

receipt, err := txRelayer.SendTransaction(txn, depositorKey)
receipt, err = txRelayer.SendTransaction(depositTxn, depositorKey)
if err != nil {
return fmt.Errorf("receiver: %s, amount: %s, error: %w", receiver, amount, err)
}
Expand All @@ -222,7 +258,7 @@ func runCommand(cmd *cobra.Command, _ []string) {
}

outputter.SetCommandResult(&depositERC20Result{
Sender: depositorKey.Address().String(),
Sender: depositorAddr.String(),
Receivers: dp.Receivers,
Amounts: dp.Amounts,
})
Expand Down Expand Up @@ -271,6 +307,28 @@ func createMintTxn(sender, receiver types.Address, amount *big.Int) (*ethgo.Tran
}, nil
}

// createApproveERC20PredicateTxn sends approve transaction
// to ERC20 token for ERC20 predicate so that it is able to spend given tokens
func createApproveERC20PredicateTxn(amount *big.Int,
rootERC20Predicate, rootERC20Token types.Address) (*ethgo.Transaction, error) {
approveFnParams := &contractsapi.ApproveFunction{
Spender: rootERC20Predicate,
Amount: amount,
}

input, err := approveFnParams.EncodeAbi()
if err != nil {
return nil, fmt.Errorf("failed to encode parameters for RootERC20.approve. error: %w", err)
}

addr := ethgo.Address(rootERC20Token)

return &ethgo.Transaction{
To: &addr,
Input: input,
}, nil
}

type depositERC20Result struct {
Sender string `json:"sender"`
Receivers []string `json:"receivers"`
Expand Down
50 changes: 9 additions & 41 deletions command/rootchain/initcontracts/init_contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"math/big"
"sort"
"strings"

"github.com/spf13/cobra"
"github.com/umbracle/ethgo"
Expand Down Expand Up @@ -33,12 +34,6 @@ const (
rootERC20PredicateName = "RootERC20Predicate"
rootERC20Name = "RootERC20"
erc20TemplateName = "ERC20Template"

// defaultAllowanceValue is value which is assigned to the RootERC20Predicate spender
defaultAllowanceValue = uint64(1e19)

// defaultFundAmount is value which is sent to rootchain contracts deployer account
defaultFundAmount = uint64(1e19)
)

var (
Expand Down Expand Up @@ -227,10 +222,17 @@ func deployContracts(outputter command.OutputFormatter, client *jsonrpc.Client,

// if running in test mode, we need to fund deployer account
if params.isTestMode {
fundAmountRaw := strings.TrimPrefix(command.DefaultPremineBalance, "0x")

fundAmount, ok := new(big.Int).SetString(fundAmountRaw, 16)
if !ok {
return fmt.Errorf("failed to parse provided fund amount: %s", fundAmountRaw)
}

// fund account
deployerAddress := deployerKey.Address()

txn := &ethgo.Transaction{To: &deployerAddress, Value: new(big.Int).SetUint64(defaultFundAmount)}
txn := &ethgo.Transaction{To: &deployerAddress, Value: fundAmount}
if _, err := txRelayer.SendTransactionLocal(txn); err != nil {
return err
}
Expand Down Expand Up @@ -355,18 +357,6 @@ func deployContracts(outputter command.OutputFormatter, client *jsonrpc.Client,
Message: fmt.Sprintf("%s %s contract is initialized", contractsDeploymentTitle, rootERC20PredicateName),
})

if params.isTestMode {
// approve RootERC20Predicate
if err := approveERC20Predicate(txRelayer, rootchainConfig, deployerKey); err != nil {
return err
}

outputter.WriteCommandResult(&messageResult{
Message: fmt.Sprintf("%s %s contract is approved for spender of %s",
contractsDeploymentTitle, rootERC20PredicateName, rootERC20Name),
})
}

return nil
}

Expand Down Expand Up @@ -444,28 +434,6 @@ func initializeRootERC20Predicate(txRelayer txrelayer.TxRelayer, rootchainConfig
return sendTransaction(txRelayer, txn, rootERC20PredicateName, deployerKey)
}

// approveERC20Predicate sends approve transaction to ERC20 token so that it is able to spend given root ERC20 token
func approveERC20Predicate(txRelayer txrelayer.TxRelayer, config *polybft.RootchainConfig,
deployerKey ethgo.Key) error {
approveFnParams := &contractsapi.ApproveFunction{
Spender: config.RootERC20PredicateAddress,
Amount: new(big.Int).SetUint64(defaultAllowanceValue),
}

input, err := approveFnParams.EncodeAbi()
if err != nil {
return fmt.Errorf("failed to encode parameters for RootERC20.approve. error: %w", err)
}

addr := ethgo.Address(config.RootNativeERC20Address)
txn := &ethgo.Transaction{
To: &addr,
Input: input,
}

return sendTransaction(txRelayer, txn, rootERC20Name, deployerKey)
}

// sendTransaction sends provided transaction
func sendTransaction(txRelayer txrelayer.TxRelayer, txn *ethgo.Transaction, contractName string,
deployerKey ethgo.Key) error {
Expand Down

0 comments on commit 6fcaa2e

Please sign in to comment.