Skip to content
This repository has been archived by the owner on Jul 1, 2024. It is now read-only.

Commit

Permalink
Merge pull request #16 from oxygenpay/develop
Browse files Browse the repository at this point in the history
merge develop
  • Loading branch information
swift1337 authored Jul 23, 2023
2 parents ff4d895 + 2e30279 commit 5830ad4
Show file tree
Hide file tree
Showing 60 changed files with 1,838 additions and 193 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ require-deps: ## Require cli tools for development
go install github.com/rubenv/sql-migrate/...@latest
go install github.com/kyleconroy/sqlc/cmd/sqlc@latest
go install github.com/cespare/reflex@latest
go install github.com/vektra/mockery/v2@latest
go install github.com/vektra/mockery/v2@v2.32.0
go install github.com/golangci/golangci-lint/cmd/[email protected]
# todo go-swagger as swagger

Expand Down
15 changes: 12 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
</p>

[OxygenPay](https://o2pay.co) is a cloud or self-hosted crypto payment gateway.
Accept ETH, MATIC, TRON, USDT, and USDC with ease. Open new opportunities for your product by accepting cryptocurrency.
Receive crypto including stablecoins with ease. Open new opportunities for your product by accepting cryptocurrency.

<img src="./.github/static/demo.jpg" alt="demo">

Expand All @@ -25,6 +25,10 @@ Accept ETH, MATIC, TRON, USDT, and USDC with ease. Open new opportunities for yo
<img src="./ui-dashboard/src/assets/icons/crypto/tron.svg" height="64" alt="tron">
<div>TRON</div>
</td>
<td align="center">
<img src="./ui-dashboard/src/assets/icons/crypto/bnb.svg" height="64" alt="bnb">
<div>BNB</div>
</td>
<td align="center">
<img src="./ui-dashboard/src/assets/icons/crypto/usdt.svg" height="64" alt="usdt">
<div>USDT</div>
Expand All @@ -33,6 +37,10 @@ Accept ETH, MATIC, TRON, USDT, and USDC with ease. Open new opportunities for yo
<img src="./ui-dashboard/src/assets/icons/crypto/usdc.svg" height="64" alt="usdc">
<div>USDC</div>
</td>
<td align="center">
<img src="./ui-dashboard/src/assets/icons/crypto/busd.svg" height="64" alt="busd">
<div>BUSD</div>
</td>
</tr>
</table>

Expand All @@ -52,12 +60,13 @@ Accept ETH, MATIC, TRON, USDT, and USDC with ease. Open new opportunities for yo

## Documentation 📚

Visit [docs.o2pay.co](https://docs.o2pay.co) for setup guides. If you have any questions, feel free to ask them in our [telegram community](https://t.me/oxygenpay_en)
Visit [docs.o2pay.co](https://docs.o2pay.co) for setup guides. If you have any questions,
feel free to ask them in our [telegram community](https://t.me/oxygenpay_en)

## Roadmap 🛣️

- [x] Support for USDC
- [ ] Support for Binance Smart Chain (BNB, BUSD)
- [x] Support for Binance Smart Chain (BNB, BUSD)
- [ ] Donations feature
- [ ] Support for [WalletConnect](https://walletconnect.com/)
- [ ] SDKs for (Python, JavaScript, PHP, etc...)
Expand Down
3 changes: 3 additions & 0 deletions api/proto/kms/kms-v1.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ paths:
/wallet/{walletId}/transaction/matic:
$ref: './v1/wallet.yml#/paths/~1wallet~1{walletId}~1transaction~1matic'

/wallet/{walletId}/transaction/bsc:
$ref: './v1/wallet.yml#/paths/~1wallet~1{walletId}~1transaction~1bsc'

/wallet/{walletId}/transaction/tron:
$ref: './v1/wallet.yml#/paths/~1wallet~1{walletId}~1transaction~1tron'

Expand Down
41 changes: 30 additions & 11 deletions api/proto/kms/v1/wallet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ definitions:

CreateMaticTransactionRequest: *createEthTransaction

CreateBSCTransactionRequest: *createEthTransaction

CreateTronTransactionRequest:
type: object
required: [ assetType, recipient, amount ]
Expand Down Expand Up @@ -114,7 +116,7 @@ definitions:
Blockchain:
type: string
description: Supported blockchain
enum: [ BTC, ETH, TRON, MATIC ]
enum: [ BTC, ETH, TRON, MATIC, BSC ]
x-nullable: false
x-omitempty: false

Expand Down Expand Up @@ -147,7 +149,7 @@ definitions:
description: Created At
example: 1656696522

EthereumTransaction:
EthereumTransaction: &ethTransaction
type: object
properties:
rawTransaction:
Expand All @@ -157,15 +159,9 @@ definitions:
x-nullable: false
x-omitempty: false

MaticTransaction:
type: object
properties:
rawTransaction:
type: string
description: RLP-encoded transaction
example: '0xf86e83014b2985048ccb44b1827530944675c7e5baafbffbca748158becba61ef3b0a26387c2a454bcf91b3f8026a0db0be3dcc25213b286e08d018fe8143eb85a3b7bb5cf3749245e907158e9c8daa033c7ec9362ee890d63b89e9dbfcfcb6edd9432321102c1d2ea7921c6cc07009e'
x-nullable: false
x-omitempty: false
MaticTransaction: *ethTransaction

BSCTransaction: *ethTransaction

TronTransaction:
type: object
Expand Down Expand Up @@ -292,6 +288,29 @@ paths:
schema:
$ref: '../kms-v1.yml#/definitions/ErrorResponse'

/wallet/{walletId}/transaction/bsc:
post:
summary: Create BSC Transaction
operationId: createBSCTransaction
tags: [ Wallet ]
parameters:
- $ref: '#/parameters/WalletId'
- in: body
name: data
required: true
schema:
$ref: '#/definitions/CreateBSCTransactionRequest'
responses:
201:
description: Transaction Created
schema:
$ref: '#/definitions/BSCTransaction'
400:
description: Validation error / Not found
schema:
$ref: '../kms-v1.yml#/definitions/ErrorResponse'


/wallet/{walletId}/transaction/tron:
post:
summary: Create Tron Transaction
Expand Down
4 changes: 2 additions & 2 deletions api/proto/merchant/v1/merchant_address.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ definitions:
properties:
blockchain:
type: string
enum: [ BTC, ETH, TRON, MATIC ]
enum: [ BTC, ETH, TRON, MATIC, BSC ]
example: ETH
x-nullable: false
address:
Expand Down Expand Up @@ -68,7 +68,7 @@ definitions:
x-omitempty: false
blockchain:
type: string
enum: [ ETH, TRON, MATIC ]
enum: [ ETH, TRON, MATIC, BSC ]
example: ETH
x-nullable: false
x-omitempty: false
Expand Down
9 changes: 9 additions & 0 deletions internal/db/repository/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,15 @@ func NumericToMoney(num pgtype.Numeric, moneyType money.Type, ticker string, dec
return money.NewFromBigInt(moneyType, ticker, bigInt, decimals)
}

func NumericToCrypto(num pgtype.Numeric, currency money.CryptoCurrency) (money.Money, error) {
bigInt, err := NumericToBigInt(num)
if err != nil {
return money.Money{}, err
}

return currency.MakeAmountFromBigInt(bigInt)
}

func MoneyToNumeric(m money.Money) pgtype.Numeric {
bigInt, _ := m.BigInt()
return BigIntToNumeric(bigInt)
Expand Down
42 changes: 42 additions & 0 deletions internal/kms/api/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ func SetupRoutes(handler *Handler) httpServer.Opt {

kmsAPI.POST("/wallet/:walletId/transaction/eth", handler.CreateEthereumTransaction)
kmsAPI.POST("/wallet/:walletId/transaction/matic", handler.CreateMaticTransaction)
kmsAPI.POST("/wallet/:walletId/transaction/bsc", handler.CreateBSCTransaction)
kmsAPI.POST("/wallet/:walletId/transaction/tron", handler.CreateTronTransaction)
}
}
Expand Down Expand Up @@ -180,6 +181,47 @@ func (h *Handler) CreateMaticTransaction(c echo.Context) error {
return c.JSON(http.StatusCreated, &model.EthereumTransaction{RawTransaction: raw})
}

func (h *Handler) CreateBSCTransaction(c echo.Context) error {
ctx := c.Request().Context()

id, err := common.UUID(c, paramWalletID)
if err != nil {
return err
}

w, err := h.wallets.GetWallet(ctx, id, false)

switch {
case errors.Is(err, wallet.ErrNotFound):
return common.NotFoundResponse(c, wallet.ErrNotFound.Error())
case err != nil:
return err
}

var req model.CreateBSCTransactionRequest
if valid := common.BindAndValidateRequest(c, &req); !valid {
return nil
}

raw, err := h.wallets.CreateBSCTransaction(ctx, w, wallet.EthTransactionParams{
Type: wallet.AssetType(req.AssetType),
Recipient: req.Recipient,
ContractAddress: req.ContractAddress,
Amount: req.Amount,
NetworkID: req.NetworkID,
Nonce: *req.Nonce,
MaxPriorityFeePerGas: req.MaxPriorityPerGas,
MaxFeePerGas: req.MaxFeePerGas,
Gas: req.Gas,
})

if err != nil {
return transactionCreationFailed(c, err)
}

return c.JSON(http.StatusCreated, &model.EthereumTransaction{RawTransaction: raw})
}

func (h *Handler) CreateTronTransaction(c echo.Context) error {
ctx := c.Request().Context()

Expand Down
83 changes: 83 additions & 0 deletions internal/kms/api/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ func TestHandlerRoutes(t *testing.T) {
walletRoute = "/api/kms/v1/wallet/:walletId"
ethereumTransactionRoute = "/api/kms/v1/wallet/:walletId/transaction/eth"
polygonTransactionRoute = "/api/kms/v1/wallet/:walletId/transaction/matic"
bscTransactionRoute = "/api/kms/v1/wallet/:walletId/transaction/bsc"
tronTransactionRoute = "/api/kms/v1/wallet/:walletId/transaction/tron"
)

Expand Down Expand Up @@ -240,6 +241,88 @@ func TestHandlerRoutes(t *testing.T) {
}
})

t.Run("CreateBSCTransaction", func(t *testing.T) {
const usdtContract = "0xdac17f958d2ee523a2206206994597c13d831ec7"

for testCaseIndex, testCase := range []struct {
wallet *wallet.Wallet
req model.CreateBSCTransactionRequest
assert func(t *testing.T, res *test.Response)
}{
{
wallet: createWallet(wallet.BSC),
req: model.CreateBSCTransactionRequest{
AssetType: "coin",
Amount: "123",
Gas: 1,
MaxFeePerGas: "123",
MaxPriorityPerGas: "456",
NetworkID: 1,
Nonce: util.Ptr(int64(0)),
Recipient: "0x690b9a9e9aa1c9db991c7721a92d351db4fac990",
},
assert: func(t *testing.T, res *test.Response) {
var body model.BSCTransaction

assert.Equal(t, http.StatusCreated, res.StatusCode(), res.String())
assert.NoError(t, res.JSON(&body))
assert.NotEmpty(t, body.RawTransaction)
},
},
{
wallet: createWallet(wallet.BSC),
req: model.CreateBSCTransactionRequest{
AssetType: "token",
Amount: "123",
ContractAddress: usdtContract,
Gas: 1,
MaxFeePerGas: "123",
MaxPriorityPerGas: "456",
NetworkID: 5,
Nonce: util.Ptr(int64(0)),
Recipient: "0x690b9a9e9aa1c9db991c7721a92d351db4fac990",
},
assert: func(t *testing.T, res *test.Response) {
var body model.BSCTransaction

assert.Equal(t, http.StatusCreated, res.StatusCode(), res.String())
assert.NoError(t, res.JSON(&body))
assert.NotEmpty(t, body.RawTransaction)
},
},
{
// blockchain mismatch
wallet: createWallet(wallet.ETH),
req: model.CreateBSCTransactionRequest{
AssetType: "coin",
Amount: "123",
Gas: 1,
MaxFeePerGas: "123",
MaxPriorityPerGas: "456",
NetworkID: 1,
Nonce: util.Ptr(int64(0)),
Recipient: "0x690b9a9e9aa1c9db991c7721a92d351db4fac990",
},
assert: func(t *testing.T, res *test.Response) {
assert.Equal(t, http.StatusBadRequest, res.StatusCode(), res.String())
},
},
} {
t.Run(strconv.Itoa(testCaseIndex+1), func(t *testing.T) {
// ACT
res := tc.Client.
POST().
Path(bscTransactionRoute).
Param(paramWalletID, testCase.wallet.UUID.String()).
JSON(&testCase.req).
Do()

// ASSERT
testCase.assert(t, res)
})
}
})

t.Run("CreateTronTransaction", func(t *testing.T) {
const usdtContract = "TBnt7Wzvd226i24r95pE82MZpHba63ehQY"

Expand Down
1 change: 1 addition & 0 deletions internal/kms/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ func (app *App) runWebServer(ctx context.Context) {
walletGenerator := wallet.NewGenerator().
AddProvider(&wallet.EthProvider{Blockchain: wallet.ETH, CryptoReader: cryptorand.Reader}).
AddProvider(&wallet.EthProvider{Blockchain: wallet.MATIC, CryptoReader: cryptorand.Reader}).
AddProvider(&wallet.EthProvider{Blockchain: wallet.BSC, CryptoReader: cryptorand.Reader}).
AddProvider(&wallet.BitcoinProvider{Blockchain: wallet.BTC, CryptoReader: cryptorand.Reader}).
AddProvider(&wallet.TronProvider{
Blockchain: wallet.TRON,
Expand Down
31 changes: 18 additions & 13 deletions internal/kms/wallet/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,7 @@ var (
ErrUnknownBlockchain = errors.New("unknown blockchain")
)

func New(
repo *Repository,
generator *Generator,
logger *zerolog.Logger,
) *Service {
func New(repo *Repository, generator *Generator, logger *zerolog.Logger) *Service {
log := logger.With().Str("channel", "kms_service").Logger()

return &Service{
Expand Down Expand Up @@ -78,9 +74,7 @@ func (s *Service) DeleteWallet(ctx context.Context, id uuid.UUID) error {
}

// CreateEthereumTransaction creates and sings new raw Ethereum transaction based on provided input.
func (s *Service) CreateEthereumTransaction(
_ context.Context, wallet *Wallet, params EthTransactionParams,
) (string, error) {
func (s *Service) CreateEthereumTransaction(_ context.Context, wt *Wallet, params EthTransactionParams) (string, error) {
if _, ok := s.generator.providers[ETH]; !ok {
return "", errors.New("ETH provider not found")
}
Expand All @@ -90,12 +84,10 @@ func (s *Service) CreateEthereumTransaction(
return "", errors.New("ETH provider is invalid")
}

return eth.NewTransaction(wallet, params)
return eth.NewTransaction(wt, params)
}

func (s *Service) CreateMaticTransaction(
_ context.Context, wallet *Wallet, params EthTransactionParams,
) (string, error) {
func (s *Service) CreateMaticTransaction(_ context.Context, wt *Wallet, params EthTransactionParams) (string, error) {
if _, ok := s.generator.providers[MATIC]; !ok {
return "", errors.New("MATIC provider not found")
}
Expand All @@ -105,7 +97,20 @@ func (s *Service) CreateMaticTransaction(
return "", errors.New("MATIC provider is invalid")
}

return matic.NewTransaction(wallet, params)
return matic.NewTransaction(wt, params)
}

func (s *Service) CreateBSCTransaction(_ context.Context, wt *Wallet, params EthTransactionParams) (string, error) {
if _, ok := s.generator.providers[BSC]; !ok {
return "", errors.New("BSC provider not found")
}

bsc, ok := s.generator.providers[BSC].(*EthProvider)
if !ok {
return "", errors.New("BSC provider is invalid")
}

return bsc.NewTransaction(wt, params)
}

func (s *Service) CreateTronTransaction(
Expand Down
Loading

0 comments on commit 5830ad4

Please sign in to comment.