diff --git a/internal/custodial/abis.go b/internal/custodial/abis.go index c20658b..3b90d5d 100644 --- a/internal/custodial/abis.go +++ b/internal/custodial/abis.go @@ -3,6 +3,7 @@ package custodial import "github.com/grassrootseconomics/w3-celo-patch" const ( + Approve = "approve" Check = "check" GiveTo = "giveTo" MintTo = "mintTo" @@ -16,12 +17,12 @@ const ( // Any relevant function signature that will be used by the custodial system can be defined here. func initAbis() map[string]*w3.Func { return map[string]*w3.Func{ - Check: w3.MustNewFunc("check(address)", "bool"), - GiveTo: w3.MustNewFunc("giveTo(address)", "uint256"), - MintTo: w3.MustNewFunc("mintTo(address, uint256)", "bool"), - NextTime: w3.MustNewFunc("nextTime(address)", "uint256"), - Register: w3.MustNewFunc("register(address)", ""), - Transfer: w3.MustNewFunc("transfer(address,uint256)", "bool"), - TransferFrom: w3.MustNewFunc("transferFrom(address, address, uint256)", "bool"), + Approve: w3.MustNewFunc("approve(address, uint256)", "bool"), + Check: w3.MustNewFunc("check(address)", "bool"), + GiveTo: w3.MustNewFunc("giveTo(address)", "uint256"), + MintTo: w3.MustNewFunc("mintTo(address, uint256)", "bool"), + NextTime: w3.MustNewFunc("nextTime(address)", "uint256"), + Register: w3.MustNewFunc("register(address)", ""), + Transfer: w3.MustNewFunc("transfer(address,uint256)", "bool"), } } diff --git a/internal/tasker/task/sign_transfer.go b/internal/tasker/task/sign_transfer.go index 33deaac..711d673 100644 --- a/internal/tasker/task/sign_transfer.go +++ b/internal/tasker/task/sign_transfer.go @@ -66,7 +66,10 @@ func SignTransfer(cu *custodial.Custodial) func(context.Context, *asynq.Task) er } }() - input, err := cu.Abis[custodial.Transfer].EncodeArgs(celoutils.HexToAddress(payload.To), new(big.Int).SetUint64(payload.Amount)) + input, err := cu.Abis[custodial.Transfer].EncodeArgs( + celoutils.HexToAddress(payload.To), + new(big.Int).SetUint64(payload.Amount), + ) if err != nil { return err } diff --git a/internal/tasker/task/sign_transfer_auth.go b/internal/tasker/task/sign_transfer_auth.go new file mode 100644 index 0000000..03229af --- /dev/null +++ b/internal/tasker/task/sign_transfer_auth.go @@ -0,0 +1,127 @@ +package task + +import ( + "context" + "encoding/json" + "math/big" + + "github.com/bsm/redislock" + "github.com/celo-org/celo-blockchain/common/hexutil" + "github.com/grassrootseconomics/celoutils" + "github.com/grassrootseconomics/cic-custodial/internal/custodial" + "github.com/grassrootseconomics/cic-custodial/internal/store" + "github.com/grassrootseconomics/cic-custodial/internal/tasker" + "github.com/grassrootseconomics/cic-custodial/pkg/enum" + "github.com/hibiken/asynq" +) + +type TransferAuthPayload struct { + AuthorizedAddress string `json:"authorizedAddress"` + AuthorizedAmount uint64 `json:"authorizedAmount"` + TrackingId string `json:"trackingId"` + VoucherAddress string `json:"voucherAddress"` +} + +func SignTransferAuthorizationProcessor(cu *custodial.Custodial) func(context.Context, *asynq.Task) error { + return func(ctx context.Context, t *asynq.Task) error { + var ( + err error + payload TransferAuthPayload + ) + + if err := json.Unmarshal(t.Payload(), &payload); err != nil { + return err + } + + lock, err := cu.LockProvider.Obtain( + ctx, + lockPrefix+cu.SystemPublicKey, + lockTimeout, + &redislock.Options{ + RetryStrategy: lockRetry(), + }, + ) + if err != nil { + return err + } + defer lock.Release(ctx) + + nonce, err := cu.Noncestore.Acquire(ctx, cu.SystemPublicKey) + if err != nil { + return err + } + defer func() { + if err != nil { + if nErr := cu.Noncestore.Return(ctx, cu.SystemPublicKey); nErr != nil { + err = nErr + } + } + }() + + input, err := cu.Abis[custodial.Approve].EncodeArgs( + celoutils.HexToAddress(payload.AuthorizedAddress), + new(big.Int).SetUint64(payload.AuthorizedAmount), + ) + if err != nil { + return err + } + + builtTx, err := cu.CeloProvider.SignContractExecutionTx( + cu.SystemPrivateKey, + celoutils.ContractExecutionTxOpts{ + ContractAddress: celoutils.HexToAddress(payload.VoucherAddress), + InputData: input, + GasFeeCap: celoutils.SafeGasFeeCap, + GasTipCap: celoutils.SafeGasTipCap, + GasLimit: uint64(celoutils.SafeGasLimit), + Nonce: nonce, + }, + ) + if err != nil { + return err + } + + rawTx, err := builtTx.MarshalBinary() + if err != nil { + return err + } + + id, err := cu.Store.CreateOtx(ctx, store.Otx{ + TrackingId: payload.TrackingId, + Type: enum.TRANSFER_AUTH, + RawTx: hexutil.Encode(rawTx), + TxHash: builtTx.Hash().Hex(), + From: cu.SystemPublicKey, + Data: hexutil.Encode(builtTx.Data()), + GasPrice: builtTx.GasPrice(), + GasLimit: builtTx.Gas(), + TransferValue: 0, + Nonce: builtTx.Nonce(), + }) + if err != nil { + return err + } + + disptachJobPayload, err := json.Marshal(TxPayload{ + OtxId: id, + Tx: builtTx, + }) + if err != nil { + return err + } + + _, err = cu.TaskerClient.CreateTask( + ctx, + tasker.DispatchTxTask, + tasker.HighPriority, + &tasker.Task{ + Payload: disptachJobPayload, + }, + ) + if err != nil { + return err + } + + return nil + } +} diff --git a/migrations/006_transfer_auth.sql b/migrations/006_transfer_auth.sql new file mode 100644 index 0000000..265ba0f --- /dev/null +++ b/migrations/006_transfer_auth.sql @@ -0,0 +1 @@ +INSERT INTO otx_tx_type (value) VALUES ('TRANSFER_AUTHORIZATION'); diff --git a/pkg/enum/enum.go b/pkg/enum/enum.go index 5671e95..db8b186 100644 --- a/pkg/enum/enum.go +++ b/pkg/enum/enum.go @@ -21,5 +21,6 @@ const ( ACCOUNT_REGISTER OtxType = "ACCOUNT_REGISTER" REFILL_GAS OtxType = "REFILL_GAS" + TRANSFER_AUTH OtxType = "TRANFSER_AUTHORIZATION" TRANSFER_VOUCHER OtxType = "TRANSFER_VOUCHER" )