Compare commits

..

118 Commits

Author SHA1 Message Date
alfred-mk
814bef2b20 fix: have the same order of received variables 2025-07-01 00:49:12 +03:00
alfred-mk
12940bb5f2 fix: use the correct PUT request method 2025-07-01 00:36:06 +03:00
alfred-mk
50ee455e70 fix: use the correct AliasUpdateURL 2025-07-01 00:31:35 +03:00
alfred-mk
57ee409f96 add UpdateAlias to the DevAccountService and mocks 2025-06-26 09:54:19 +03:00
alfred-mk
49a8184d02 add the UpdateAlias function 2025-06-26 09:53:26 +03:00
alfred-mk
9a6ab7e6e2 add the endpoint for updating the alias 2025-06-26 09:52:31 +03:00
d177942bd2 Merge pull request 'pool-swap-endpoints' (#11) from pool-swap-endpoints into master
Reviewed-on: #11
2025-06-26 07:58:53 +02:00
alfred-mk
339ba854c9 fix: correctly unmarshal nested pool details response in retrievePoolDeta 2025-06-24 12:07:44 +03:00
alfred-mk
5aa032400c update the alias endpoints 2025-06-24 10:48:30 +03:00
alfred-mk
7b42d509e6 log the request and response body 2025-06-23 10:50:57 +03:00
alfred-mk
d945964b0b use the TokenAddress instead of ContractAddress 2025-06-23 10:00:26 +03:00
alfred-mk
c1797e7a32 use the TokenHoldings instead of TokenDetails 2025-06-23 09:32:34 +03:00
alfred-mk
54dfe037b4 updated the ussd-data-service to tag v1.5.0-beta 2025-06-23 09:31:53 +03:00
alfred-mk
b5ccaea575 add the RetrievePoolDetails to the testutil and dev account service 2025-06-06 22:42:35 +03:00
alfred-mk
bf830e92de add the RetrievePoolDetails to the account_service 2025-06-05 20:41:08 +03:00
alfred-mk
24224e553d use the correct PoolSwapURL when performing the actual swap 2025-05-22 15:31:08 +03:00
alfred-mk
6c3719e3b6 Fix: use the correct TokenDetails in the GetPoolSwappableVouchers 2025-05-21 17:12:46 +03:00
alfred-mk
62cc5eed89 use the updated ussd-data-service 2025-05-21 17:10:25 +03:00
alfred-mk
343d30a2f2 rename the variables and read the stables param from the config 2025-05-21 15:42:06 +03:00
alfred-mk
a354371a94 add the IncludeStablesParam config 2025-05-21 15:40:56 +03:00
alfred-mk
857f564aae Fix: remove the unused publicKey from the GetPoolSwappableVouchers 2025-05-21 15:22:31 +03:00
alfred-mk
f234d51104 Fix: update the getPoolSwappableVouchers to pass a 'stable' query parameter 2025-05-21 15:21:27 +03:00
alfred-mk
81fbc2574c Fix: use the correct getPoolSwappableVouchers 2025-05-20 15:50:35 +03:00
alfred-mk
334aa07f9f Fix: Correct JSON mapping for checkTokenInPool and getSwapFromTokenMaxLimit API responses 2025-05-20 15:48:13 +03:00
alfred-mk
e6677015d9 use the latest ussd-data-service 2025-05-19 10:36:47 +03:00
alfred-mk
050998ff82 added CheckTokenInPool to the account_service and test utils 2025-05-17 14:45:12 +03:00
alfred-mk
ee434dba69 added CheckTokenInPool to check whether a token can be swapped from in a pool 2025-05-17 14:37:06 +03:00
alfred-mk
f101ffd4c9 added a model for TokenInPoolResult 2025-05-17 14:36:26 +03:00
alfred-mk
8b2bd72143 format document 2025-05-17 14:35:53 +03:00
alfred-mk
3b85167ad8 Merge branch 'master' into pool-swap-endpoints 2025-05-16 12:43:26 +03:00
carlos
5d221b8d56 Merge pull request 'sms-address-pin-reset' (#14) from sms-address-pin-reset into master
Reviewed-on: #14
2025-04-28 10:27:11 +02:00
Carlosokumu
341901ec83 match new external sms endpoint 2025-04-28 11:26:13 +03:00
Carlosokumu
ed416c57c7 chore: rename EXTRA_SMS_BASE to EXTERNAL_SMS_BASE 2025-04-28 11:15:11 +03:00
Carlosokumu
6fc99661f7 chore: remove extra underscore in EXTRA_SMS_BASE env identifier 2025-04-28 11:08:39 +03:00
Carlosokumu
d509477de3 remove accidentallypushed bearer token 2025-04-28 11:03:34 +03:00
Carlosokumu
4ea8fd1f04 chore: rename extrasms prefixes to external sms 2025-04-28 10:56:05 +03:00
Carlosokumu
5c0ed48e67 add required sms methods 2025-04-17 23:14:42 +03:00
Carlosokumu
5ff139b649 add expected accountservice methods 2025-04-17 23:07:33 +03:00
Carlosokumu
86cfb9c020 match expected method signature 2025-04-17 22:18:30 +03:00
Carlosokumu
58366274c3 add required AccountService methods 2025-04-17 18:58:12 +03:00
Carlosokumu
b85511e436 implement SendPINResetSMS and SendAddressSMS 2025-04-17 18:57:24 +03:00
Carlosokumu
a243fe8c29 add SendAddressSMS and SendPINResetSMS 2025-04-17 18:57:02 +03:00
Carlosokumu
492b101aa4 setup extra sms api endpoints 2025-04-17 18:56:20 +03:00
34957e5b6f Merge pull request 'sms-upsell-api' (#10) from sms-api into master
Reviewed-on: #10
2025-04-11 10:06:08 +02:00
alfred-mk
6597d6b6a2 added the SendUpsellSMS to DevAccountService and MockApi 2025-04-11 11:02:23 +03:00
Carlosokumu
441e289854 implement SendUpsellSMs 2025-04-01 15:25:10 +03:00
Carlosokumu
5b41c8dc64 chore: rename SendSMS to SendUpsellSMS 2025-04-01 14:55:03 +03:00
Carlosokumu
417e7f0179 fix: correct sendSms response 2025-04-01 14:53:30 +03:00
Carlosokumu
2eed990921 Merge branch 'master' into sms-api 2025-04-01 14:18:04 +03:00
Carlosokumu
56ed9b2439 Merge branch 'multi-pool' into pool-swap-endpoints 2025-03-20 08:35:22 +03:00
Carlosokumu
064e749b04 Merge branch 'master' into pool-swap-endpoints 2025-03-19 15:22:30 +03:00
Carlosokumu
80c2493e80 implement get token pool limit,get all pool swappable vouchers 2025-03-19 12:09:52 +03:00
Carlosokumu
4444ac0255 add pool swap endpoints 2025-03-19 12:07:11 +03:00
Carlosokumu
720fa94b56 register pool endpoints 2025-03-17 20:40:56 +03:00
Carlosokumu
ee04552cbb feat: implement fetch top pools,retrieve pool details using a pool symbol 2025-03-17 20:40:19 +03:00
Carlosokumu
c09cb77e19 implement pool limit 2025-03-17 12:08:17 +03:00
Carlosokumu
e053d561c9 feat(pool): add multiple pools 2025-03-14 10:04:04 +03:00
Carlosokumu
b9cef2b578 Merge branch 'pool-swap-endpoints' into multi-pool 2025-03-14 09:03:09 +03:00
Carlosokumu
06a7ddea6f read persisted vouchers for a particular pool 2025-03-13 10:35:21 +03:00
alfred-mk
ba43610ff0 Added pool swap functions to the MockAccountService 2025-03-13 10:14:11 +03:00
alfred-mk
3c2889ac72 Added pool swap functions to the TestAccountService 2025-03-13 10:05:49 +03:00
Carlosokumu
5432365874 add logs,use same pool instance 2025-03-12 23:20:01 +03:00
alfred-mk
3244c717cb Added the MaxLimitResult struct model 2025-03-11 15:46:51 +03:00
alfred-mk
17e89e0b88 Added the GetSwapFromTokenMaxLimit 2025-03-11 15:46:17 +03:00
alfred-mk
3b39b86d09 pass the correct arguments to the swap functions 2025-03-10 17:12:05 +03:00
Carlosokumu
37a2006a0b feat: persist and load pool info 2025-03-10 16:50:29 +03:00
Carlosokumu
d4f27af7a7 add check for account when doing a pool deposit 2025-03-10 16:50:29 +03:00
alfred-mk
ba8d2a19c2 Use the DevAccountService functions to retrieve the data 2025-03-10 16:39:37 +03:00
alfred-mk
e3b6c25792 Added the swap functions to the accountService interface 2025-03-10 14:17:03 +03:00
alfred-mk
a04f7ee66c Remove unused model structs 2025-03-10 14:03:30 +03:00
alfred-mk
52e1be1104 Return an array of data instead of reading from the file 2025-03-10 14:00:16 +03:00
alfred-mk
d7ae1cc096 Include the account's public key 2025-03-10 13:59:05 +03:00
Carlosokumu
8145b4bd00 fix: remove formatting to fqdn 2025-03-10 12:39:12 +03:00
Carlosokumu
dda02852bf add alias resolve logs 2025-03-10 11:49:49 +03:00
Carlosokumu
6e437cb8e0 feat: sanitize alias hint before resolving 2025-03-07 16:10:40 +03:00
Carlosokumu
4af0e9709d fix: format alias to fqdn before request 2025-03-07 16:04:15 +03:00
Carlosokumu
9c8a3df971 add logs 2025-03-07 09:45:09 +03:00
carlos
62c33e2587 Merge pull request 'ens-names' (#8) from ens-names into master
Reviewed-on: #8
2025-03-07 07:27:29 +01:00
Carlosokumu
4783e2dcb3 Merge branch 'pool-swap-endpoints-issue#9' into pool-swap-endpoints 2025-03-07 08:16:49 +03:00
Carlosokumu
e8c58a8f33 feat: implement fetch pools,pool swappable vouchers with dummy data 2025-03-07 08:13:15 +03:00
Carlosokumu
67d7ec1567 add pool structs 2025-03-07 08:11:33 +03:00
Carlosokumu
b05734f976 feat: add fetch pools,swappable pool vouchers 2025-03-07 08:11:04 +03:00
Carlosokumu
f723e0aa45 add sample test data 2025-03-07 08:09:41 +03:00
alfred-mk
35f24b5183 Use the latest ussd-data-service 2025-03-06 18:14:34 +03:00
alfred-mk
4a5de68a8c added the SendSMS func to the httpAccount service 2025-03-06 09:06:45 +03:00
alfred-mk
6c4702e2ba added the send sms response model 2025-03-06 09:05:39 +03:00
alfred-mk
7280784ee1 added the send sms endpoint configs 2025-03-06 09:05:01 +03:00
Carlosokumu
aef8efa2bf add pool api responses 2025-03-05 16:42:55 +03:00
Carlosokumu
a1fe416f51 implement pool deposit,swap and quote endpoints 2025-03-05 16:41:54 +03:00
Carlosokumu
3473a4413b add pool deposit,swap and quote endpoints 2025-03-05 16:40:55 +03:00
Carlosokumu
1f3ac220d1 feat: request and resolve aliases. 2025-02-24 14:49:58 +03:00
Carlosokumu
e64eb265a5 add alias responses 2025-02-24 14:48:00 +03:00
Carlosokumu
d5dc792dce add alias ens endpoint 2025-02-24 14:45:45 +03:00
Carlosokumu
31eb30de0f add dev api logs 2025-02-06 14:29:44 +03:00
2181388f5b Merge pull request 'dev-api-aliases-v2' (#6) from dev-api-aliases-v2 into master
Reviewed-on: #6
2025-01-23 15:28:05 +01:00
Carlosokumu
448fdffbd0 untie session id to saved aliases 2025-01-23 12:05:23 +03:00
Carlosokumu
4ae0a484f4 run go mod tidy 2025-01-23 10:43:19 +03:00
Carlosokumu
3f9dc89a40 resolve alias address based on UseApi field 2025-01-22 17:19:46 +03:00
Carlosokumu
bc7afa50a4 dep: downgrade vise-driver 2025-01-22 15:53:28 +03:00
Carlosokumu
9c75109b75 use dev storage service 2025-01-22 15:35:16 +03:00
lash
ee574908d4 Gofmt 2025-01-21 15:30:22 +00:00
lash
e0b5398098 Merge remote-tracking branch 'origin/master' into lash/alias-deps 2025-01-21 13:51:50 +00:00
lash
b7e53609a9 Upgrade deps 2025-01-21 13:51:07 +00:00
a4cc7d2a98 Merge pull request 'dev-api-aliases' (#5) from dev-api-aliases into master
Reviewed-on: #5
2025-01-21 07:17:09 +01:00
Carlosokumu
a63164bcf5 fix failing test 2025-01-21 08:45:03 +03:00
Carlosokumu
fa355e3729 handle accounts created via the api,add getter for the account aliases 2025-01-20 22:28:16 +03:00
Carlosokumu
99c704f6ff implement request alias based on dev implementation 2025-01-20 20:12:56 +03:00
Carlosokumu
ed549cba70 bind session id for aliases,soft code voucher balance 2025-01-20 20:11:08 +03:00
Carlosokumu
8a47d1d674 implement request alias 2025-01-20 17:28:03 +03:00
Carlosokumu
23b4180e50 persist aliases,construct the fqdn 2025-01-20 17:27:19 +03:00
b22ff1e7f0 Merge pull request 'Implement alias handling dev api' (#3) from lash/alias into master
Reviewed-on: #3
2025-01-15 08:37:53 +01:00
lash
bca7c5de96 Make prefix settable in dev api 2025-01-15 07:22:14 +00:00
lash
c128678f4a Upgrade dep 2025-01-15 00:33:59 +00:00
lash
7001d1f828 Use storage service and db dumper in dev api 2025-01-15 00:12:38 +00:00
lash
9645a2af7b Correct result structs for mock/test api implementations 2025-01-14 14:20:51 +00:00
lash
7a96b85b28 Correct alias result struct 2025-01-14 13:57:18 +00:00
lash
18a30a0065 Add dev api for alias handling' 2025-01-14 13:53:29 +00:00
lash
a4976e6c52 Move api mock from sarafu vise events 2025-01-14 07:29:12 +00:00
17 changed files with 1570 additions and 194 deletions

View File

@@ -7,33 +7,56 @@ import (
)
const (
createAccountPath = "/api/v2/account/create"
trackStatusPath = "/api/track"
balancePathPrefix = "/api/account"
trackPath = "/api/v2/account/status"
tokenTransferPrefix = "/api/v2/token/transfer"
voucherHoldingsPathPrefix = "/api/v1/holdings"
voucherTransfersPathPrefix = "/api/v1/transfers/last10"
voucherDataPathPrefix = "/api/v1/token"
AliasPrefix = "api/v1/alias"
createAccountPath = "/api/v2/account/create"
trackStatusPath = "/api/track"
balancePathPrefix = "/api/account"
trackPath = "/api/v2/account/status"
tokenTransferPrefix = "/api/v2/token/transfer"
voucherHoldingsPathPrefix = "/api/v1/holdings"
voucherTransfersPathPrefix = "/api/v1/transfers/last10"
voucherDataPathPrefix = "/api/v1/token"
SendSMSPrefix = "api/v1/external/upsell"
poolDepositPrefix = "/api/v2/pool/deposit"
poolSwapQoutePrefix = "/api/v2/pool/quote"
poolSwapPrefix = "/api/v2/pool/swap"
topPoolsPrefix = "/api/v1/pool/top"
retrievePoolDetailsPrefix = "/api/v1/pool/reverse"
poolSwappableVouchersPrefix = "/api/v1/pool"
AliasRegistrationPrefix = "/api/v1/internal/register"
AliasResolverPrefix = "/api/v1/resolve"
ExternalSMSPrefix = "/api/v1/external"
AliasUpdatePrefix = "/api/v1/internal/update"
)
var (
custodialURLBase string
dataURLBase string
BearerToken string
custodialURLBase string
dataURLBase string
BearerToken string
aliasEnsURLBase string
externalSMSBase string
IncludeStablesParam string
)
var (
CreateAccountURL string
TrackStatusURL string
BalanceURL string
TrackURL string
TokenTransferURL string
VoucherHoldingsURL string
VoucherTransfersURL string
VoucherDataURL string
CheckAliasURL string
CreateAccountURL string
TrackStatusURL string
BalanceURL string
TrackURL string
TokenTransferURL string
VoucherHoldingsURL string
VoucherTransfersURL string
VoucherDataURL string
PoolDepositURL string
PoolSwapQuoteURL string
PoolSwapURL string
TopPoolsURL string
RetrievePoolDetailsURL string
PoolSwappableVouchersURL string
SendSMSURL string
AliasRegistrationURL string
AliasResolverURL string
ExternalSMSURL string
AliasUpdateURL string
)
func setBase() error {
@@ -41,7 +64,10 @@ func setBase() error {
custodialURLBase = env.GetEnv("CUSTODIAL_URL_BASE", "http://localhost:5003")
dataURLBase = env.GetEnv("DATA_URL_BASE", "http://localhost:5006")
aliasEnsURLBase = env.GetEnv("ALIAS_ENS_BASE", "http://localhost:5015")
externalSMSBase = env.GetEnv("EXTERNAL_SMS_BASE", "http://localhost:5035")
BearerToken = env.GetEnv("BEARER_TOKEN", "")
IncludeStablesParam = env.GetEnv("INCLUDE_STABLES_PARAM", "false")
_, err = url.Parse(custodialURLBase)
if err != nil {
@@ -68,7 +94,17 @@ func LoadConfig() error {
VoucherHoldingsURL, _ = url.JoinPath(dataURLBase, voucherHoldingsPathPrefix)
VoucherTransfersURL, _ = url.JoinPath(dataURLBase, voucherTransfersPathPrefix)
VoucherDataURL, _ = url.JoinPath(dataURLBase, voucherDataPathPrefix)
CheckAliasURL, _ = url.JoinPath(dataURLBase, AliasPrefix)
SendSMSURL, _ = url.JoinPath(dataURLBase, SendSMSPrefix)
PoolDepositURL, _ = url.JoinPath(custodialURLBase, poolDepositPrefix)
PoolSwapQuoteURL, _ = url.JoinPath(custodialURLBase, poolSwapQoutePrefix)
PoolSwapURL, _ = url.JoinPath(custodialURLBase, poolSwapPrefix)
TopPoolsURL, _ = url.JoinPath(dataURLBase, topPoolsPrefix)
RetrievePoolDetailsURL, _ = url.JoinPath(dataURLBase, retrievePoolDetailsPrefix)
PoolSwappableVouchersURL, _ = url.JoinPath(dataURLBase, poolSwappableVouchersPrefix)
AliasRegistrationURL, _ = url.JoinPath(aliasEnsURLBase, AliasRegistrationPrefix)
AliasResolverURL, _ = url.JoinPath(aliasEnsURLBase, AliasResolverPrefix)
ExternalSMSURL, _ = url.JoinPath(externalSMSBase, ExternalSMSPrefix)
AliasUpdateURL, _ = url.JoinPath(aliasEnsURLBase, AliasUpdatePrefix)
return nil
}

View File

@@ -1,73 +1,81 @@
package dev
import (
"bytes"
"context"
"crypto/sha1"
"encoding/json"
"fmt"
"math/rand"
"os"
"regexp"
"strconv"
"strings"
"time"
"github.com/gofrs/uuid"
"git.defalsify.org/vise.git/logging"
"git.defalsify.org/vise.git/db"
fsdb "git.defalsify.org/vise.git/db/fs"
"git.grassecon.net/grassrootseconomics/sarafu-api/models"
"git.defalsify.org/vise.git/logging"
"git.grassecon.net/grassrootseconomics/common/phone"
"git.grassecon.net/grassrootseconomics/sarafu-api/event"
"git.grassecon.net/grassrootseconomics/sarafu-api/models"
"git.grassecon.net/grassrootseconomics/visedriver/storage"
"github.com/gofrs/uuid"
dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api"
)
var (
logg = logging.NewVanilla().WithDomain("sarafu-api.devapi")
logg = logging.NewVanilla().WithDomain("sarafu-api.devapi")
aliasRegex = regexp.MustCompile("^\\+?[a-zA-Z0-9\\-_]+$")
searchDomain = ".sarafu.local"
)
const (
pubKeyLen int = 20
hashLen int = 32
defaultDecimals = 6
zeroAddress string = "0x0000000000000000000000000000000000000000"
pubKeyLen int = 20
hashLen int = 32
defaultDecimals = 6
zeroAddress string = "0x0000000000000000000000000000000000000000"
defaultVoucherBalance float64 = 500.00
cityPoolAddress string = "0x3b517308D858a47458aD5C8E699697C5dc91Da0F"
poolName string = "citypool"
PoolSymbol string = "CTY"
)
type Tx struct {
Track string `json: "track"`
Hsh string `json:"hash"`
To string `json:"to"`
From string `json: "from"`
Voucher string `json: "voucher"`
Value int `json: "value"`
When time.Time `json: "when"`
Track string `json: "track"`
Hsh string `json:"hash"`
To string `json:"to"`
From string `json: "from"`
Voucher string `json: "voucher"`
Value int `json: "value"`
When time.Time `json: "when"`
}
func (t *Tx) ToTransferEvent() event.EventTokenTransfer {
return event.EventTokenTransfer{
To: t.To,
Value: t.Value,
To: t.To,
Value: t.Value,
VoucherAddress: t.Voucher,
TxHash: t.Hsh,
From: t.From,
TxHash: t.Hsh,
From: t.From,
}
}
func (t *Tx) ToMintEvent() event.EventTokenMint {
return event.EventTokenMint{
To: t.To,
Value: t.Value,
To: t.To,
Value: t.Value,
VoucherAddress: t.Voucher,
TxHash: t.Hsh,
TxHash: t.Hsh,
}
}
type Account struct {
Track string `json: "track"`
Address string `json: "address"`
Nonce int `json: "nonce"`
DefaultVoucher string `json: "defaultVoucher"`
Balances map[string]int `json: "balances"`
Alias string
Txs []string `json: "txs"`
Track string `json: "track"`
Address string `json: "address"`
Nonce int `json: "nonce"`
DefaultVoucher string `json: "defaultVoucher"`
Balances map[string]int `json: "balances"`
Alias string
Txs []string `json: "txs"`
}
func (a *Account) ToRegistrationEvent() event.EventCustodialRegistration {
@@ -77,60 +85,72 @@ func (a *Account) ToRegistrationEvent() event.EventCustodialRegistration {
}
type Voucher struct {
Name string `json: "name"`
Address string `json: "address"`
Symbol string `json: "symbol"`
Decimals int `json: "decimals"`
Sink string `json: "sink"`
Name string `json: "name"`
Address string `json: "address"`
Symbol string `json: "symbol"`
Decimals int `json: "decimals"`
Sink string `json: "sink"`
Commodity string `json: "commodity"`
Location string `json: "location"`
Location string `json: "location"`
}
type Pool struct {
Name string `json: "name"`
Symbol string `json: "symbol"`
Address string `json: "address"`
Vouchers []Voucher `json: "voucher"`
PoolLimit map[string]string `json: "poollimit"`
}
type DevAccountService struct {
dir string
db db.Db
accounts map[string]Account
accountsTrack map[string]string
accountsAlias map[string]string
vouchers map[string]Voucher
vouchersAddress map[string]string
txs map[string]Tx
txsTrack map[string]string
toAutoCreate bool
autoVouchers []string
db db.Db
accounts map[string]Account
accountsTrack map[string]string
accountsAlias map[string]string
vouchers map[string]Voucher
vouchersAddress map[string]string
txs map[string]Tx
txsTrack map[string]string
toAutoCreate bool
autoVouchers []string
autoVoucherValue map[string]int
defaultAccount string
emitterFunc event.EmitterFunc
// accountsSession map[string]string
defaultAccount string
emitterFunc event.EmitterFunc
pfx []byte
pools map[string]Pool
}
func NewDevAccountService(ctx context.Context, d string) *DevAccountService {
func NewDevAccountService(ctx context.Context, ss storage.StorageService) *DevAccountService {
svc := &DevAccountService{
dir: d,
db: fsdb.NewFsDb(),
accounts: make(map[string]Account),
accountsTrack: make(map[string]string),
accountsAlias: make(map[string]string),
vouchers: make(map[string]Voucher),
vouchersAddress: make(map[string]string),
txs: make(map[string]Tx),
txsTrack: make(map[string]string),
accounts: make(map[string]Account),
accountsTrack: make(map[string]string),
accountsAlias: make(map[string]string),
vouchers: make(map[string]Voucher),
vouchersAddress: make(map[string]string),
txs: make(map[string]Tx),
txsTrack: make(map[string]string),
autoVoucherValue: make(map[string]int),
defaultAccount: zeroAddress,
pools: make(map[string]Pool),
defaultAccount: zeroAddress,
pfx: []byte("__"),
}
if ss != nil {
var err error
svc.db, err = ss.GetUserdataDb(ctx)
if err != nil {
panic(err)
}
svc.db.SetSession("")
svc.db.SetPrefix(db.DATATYPE_USERDATA)
err = svc.loadAll(ctx)
if err != nil {
logg.DebugCtxf(ctx, "loadall error", "err", err)
}
}
acc := Account{
Address: zeroAddress,
}
svc.accounts[acc.Address] = acc
err := svc.db.Connect(ctx, d)
if err != nil {
panic(err)
}
svc.db.SetPrefix(db.DATATYPE_USERDATA)
err = svc.loadAll(ctx)
if err != nil {
panic(err)
}
return svc
}
@@ -139,6 +159,15 @@ func (das *DevAccountService) WithEmitter(fn event.EmitterFunc) *DevAccountServi
return das
}
func (das *DevAccountService) WithPrefix(pfx []byte) *DevAccountService {
das.pfx = pfx
return das
}
func (das *DevAccountService) prefixKeyFor(k string, v string) []byte {
return append(das.pfx, []byte(k+"_"+v)...)
}
func (das *DevAccountService) loadAccount(ctx context.Context, pubKey string, v []byte) error {
var acc Account
@@ -155,6 +184,15 @@ func (das *DevAccountService) loadAccount(ctx context.Context, pubKey string, v
return nil
}
func (p *Pool) hasVoucher(voucherAddress string) bool {
for _, value := range p.Vouchers {
if value.Address == voucherAddress {
return true
}
}
return false
}
func (das *DevAccountService) loadTx(ctx context.Context, hsh string, v []byte) error {
var mytx Tx
@@ -168,17 +206,46 @@ func (das *DevAccountService) loadTx(ctx context.Context, hsh string, v []byte)
return nil
}
func (das *DevAccountService) loadAlias(ctx context.Context, alias string, key []byte) error {
result, err := das.db.Get(ctx, key)
if err != nil {
return err
}
das.accountsAlias[alias] = strings.ReplaceAll(string(result), `"`, "")
return nil
}
func (das *DevAccountService) loadPoolInfo(ctx context.Context, name string, v []byte) error {
var pool Pool
err := json.Unmarshal(v, &pool)
if err != nil {
return fmt.Errorf("failed to unmarshall pool info: %v", err)
}
das.pools[name] = pool
return nil
}
func (das *DevAccountService) loadItem(ctx context.Context, k []byte, v []byte) error {
var err error
s := string(k)
ss := strings.SplitN(s, "_", 2)
ss := strings.SplitN(s[2:], "_", 2)
if len(ss) != 2 {
return fmt.Errorf("malformed key: %s", s)
}
if ss[0] == "account" {
err = das.loadAccount(ctx, ss[1], v)
logg.ErrorCtxf(ctx, "loading saved account failed", "error_load_account", err)
} else if ss[0] == "tx" {
err = das.loadTx(ctx, ss[1], v)
logg.ErrorCtxf(ctx, "loading transactions failed", "error_load_txs", err)
} else if ss[0] == "alias" {
err = das.loadAlias(ctx, ss[1], k)
logg.ErrorCtxf(ctx, "loading aliases failed", "error_load_aliases", err)
} else if ss[0] == "pool" {
err = das.loadPoolInfo(ctx, ss[1], v)
} else {
logg.ErrorCtxf(ctx, "unknown double underscore key", "key", ss[0])
}
return err
}
@@ -186,17 +253,18 @@ func (das *DevAccountService) loadItem(ctx context.Context, k []byte, v []byte)
// TODO: Add connect tx and account
// TODO: update balance
func (das *DevAccountService) loadAll(ctx context.Context) error {
d, err := os.ReadDir(das.dir)
dumper, err := das.db.Dump(ctx, []byte{})
if err != nil {
return err
}
for _, v := range(d) {
// TODO: move decoding to vise
fp := v.Name()
k := []byte(fp[1:])
v, err := das.db.Get(ctx, k)
if err != nil {
return err
for true {
k, v := dumper.Next(ctx)
logg.InfoCtxf(ctx, "loading all", "key", string(k), "value", string(v))
if k == nil {
break
}
if !bytes.HasPrefix(k, das.pfx) {
continue
}
err = das.loadItem(ctx, k, v)
if err != nil {
@@ -207,7 +275,7 @@ func (das *DevAccountService) loadAll(ctx context.Context) error {
}
func (das *DevAccountService) indexAll(ctx context.Context) error {
for k, v := range(das.txs) {
for k, v := range das.txs {
acc := das.accounts[v.From]
acc.Txs = append(acc.Txs, k)
logg.TraceCtxf(ctx, "add tx to sender index", "from", v.From, "tx", k)
@@ -232,6 +300,35 @@ func (das *DevAccountService) WithAutoVoucher(ctx context.Context, symbol string
return das
}
func (das *DevAccountService) RegisterPool(ctx context.Context, name string, sm string) error {
var seedVouchers []Voucher
h := sha1.New()
h.Write([]byte(sm))
z := h.Sum(nil)
pooladdr := fmt.Sprintf("0x%x", z)
p := Pool{
Name: name,
Symbol: sm,
Address: pooladdr,
PoolLimit: make(map[string]string),
}
for _, v := range das.vouchers {
//pre-load vouchers with vouchers when a pool is registered
seedVouchers = append(seedVouchers, v)
p.PoolLimit[v.Address] = fmt.Sprintf("%f", defaultVoucherBalance)
}
p.Vouchers = append(p.Vouchers, seedVouchers...)
err := das.savePoolInfo(ctx, p)
if err != nil {
return err
}
return nil
}
// TODO: add persistence for vouchers
// TODO: set max balance for 0x00 address
func (das *DevAccountService) AddVoucher(ctx context.Context, symbol string) error {
@@ -247,8 +344,8 @@ func (das *DevAccountService) AddVoucher(ctx context.Context, symbol string) err
z := h.Sum(nil)
address := fmt.Sprintf("0x%x", z)
das.vouchers[symbol] = Voucher{
Name: symbol,
Symbol: symbol,
Name: symbol,
Symbol: symbol,
Address: address,
}
das.vouchersAddress[address] = symbol
@@ -270,14 +367,14 @@ func (das *DevAccountService) CheckBalance(ctx context.Context, publicKey string
if !ok {
return nil, fmt.Errorf("balance not found for default token %s pubkey %v", acc.DefaultVoucher, publicKey)
}
return &models.BalanceResult {
return &models.BalanceResult{
Balance: strconv.Itoa(bal),
Nonce: json.Number(strconv.Itoa(acc.Nonce)),
Nonce: json.Number(strconv.Itoa(acc.Nonce)),
}, nil
}
func (das *DevAccountService) balanceAuto(ctx context.Context, pubKey string) error {
for _, v := range(das.autoVouchers) {
for _, v := range das.autoVouchers {
voucher, ok := das.vouchers[v]
if !ok {
return fmt.Errorf("balance auto voucher set but not resolved: %s", v)
@@ -294,15 +391,55 @@ func (das *DevAccountService) balanceAuto(ctx context.Context, pubKey string) er
return nil
}
func (das *DevAccountService) GetAliases(ctx context.Context) map[string]string {
return das.accountsAlias
}
func (das *DevAccountService) saveAccount(ctx context.Context, acc Account) error {
k := "account_" + acc.Address
if das.db == nil {
return nil
}
k := das.prefixKeyFor("account", acc.Address)
v, err := json.Marshal(acc)
if err != nil {
return err
}
das.db.SetSession("")
das.db.SetPrefix(db.DATATYPE_USERDATA)
return das.db.Put(ctx, []byte(k), v)
}
func (das *DevAccountService) savePoolInfo(ctx context.Context, pool Pool) error {
if das.db == nil {
return nil
}
k := das.prefixKeyFor("pool", pool.Address)
v, err := json.Marshal(pool)
if err != nil {
return err
}
das.db.SetSession("")
das.db.SetPrefix(db.DATATYPE_USERDATA)
return das.db.Put(ctx, []byte(k), v)
}
func (das *DevAccountService) saveAlias(ctx context.Context, alias map[string]string) error {
if das.db == nil {
return fmt.Errorf("Db cannot be nil")
}
for k, v := range alias {
k_ := das.prefixKeyFor("alias", k)
v_, err := json.Marshal(v)
if err != nil {
return err
}
das.db.SetSession("")
das.db.SetPrefix(db.DATATYPE_USERDATA)
return das.db.Put(ctx, []byte(k_), v_)
}
return nil
}
func (das *DevAccountService) CreateAccount(ctx context.Context) (*models.AccountResult, error) {
var b [pubKeyLen]byte
uid, err := uuid.NewV4()
@@ -318,7 +455,7 @@ func (das *DevAccountService) CreateAccount(ctx context.Context) (*models.Accoun
}
pubKey := fmt.Sprintf("0x%x", b)
acc := Account{
Track: uid.String(),
Track: uid.String(),
Address: pubKey,
}
@@ -340,7 +477,7 @@ func (das *DevAccountService) CreateAccount(ctx context.Context) (*models.Accoun
if das.emitterFunc != nil {
msg := event.Msg{
Typ: event.EventRegistrationTag,
Typ: event.EventRegistrationTag,
Item: acc,
}
err = das.emitterFunc(ctx, msg)
@@ -351,11 +488,85 @@ func (das *DevAccountService) CreateAccount(ctx context.Context) (*models.Accoun
logg.TraceCtxf(ctx, "account created", "account", acc)
return &models.AccountResult{
PublicKey: pubKey,
PublicKey: pubKey,
TrackingId: uid.String(),
}, nil
}
func (das *DevAccountService) PoolDeposit(ctx context.Context, amount, from, poolAddress, tokenAddress string) (*models.PoolDepositResult, error) {
_, ok := das.accounts[from]
if !ok {
return nil, fmt.Errorf("account not found (publickey): %v", from)
}
sym, ok := das.vouchersAddress[tokenAddress]
if !ok {
return nil, fmt.Errorf("voucher address %v not found", tokenAddress)
}
uid, err := uuid.NewV4()
if err != nil {
return nil, err
}
_, ok = das.vouchers[sym]
if !ok {
return nil, fmt.Errorf("voucher address %v found but does not resolve", tokenAddress)
}
if err != nil {
return nil, err
}
return &models.PoolDepositResult{
TrackingId: uid.String(),
}, nil
}
func (das *DevAccountService) GetPoolSwapQuote(ctx context.Context, amount, from, fromTokenAddress, poolAddress, toTokenAddress string) (*models.PoolSwapQuoteResult, error) {
_, ok := das.accounts[from]
if !ok {
return nil, fmt.Errorf("account not found (publickey): %v", from)
}
p, ok := das.pools[poolAddress]
if !ok {
return nil, fmt.Errorf("pool address %v not found", poolAddress)
}
//resolve the token address you are trying to swap from(fromTokenAddress)
ok = p.hasVoucher(fromTokenAddress)
if !ok {
return nil, fmt.Errorf("voucher with address %v not found in the pool", fromTokenAddress)
}
//Return a a quote that is equal to the amount entered
return &models.PoolSwapQuoteResult{IncludesFeesDeduction: false, OutValue: amount}, nil
}
func (das *DevAccountService) PoolSwap(ctx context.Context, amount, from, fromTokenAddress, poolAddress, toTokenAddress string) (*models.PoolSwapResult, error) {
uid, err := uuid.NewV4()
if err != nil {
return nil, err
}
p, ok := das.pools[poolAddress]
if !ok {
return nil, fmt.Errorf("pool address %v not found", toTokenAddress)
}
_, ok = das.accounts[from]
if !ok {
return nil, fmt.Errorf("account not found (publickey): %v", from)
}
ok = p.hasVoucher(fromTokenAddress)
if !ok {
return nil, fmt.Errorf("token %v not found in the pool", fromTokenAddress)
}
ok = p.hasVoucher(toTokenAddress)
if !ok {
return nil, fmt.Errorf("token %v not found in the pool", toTokenAddress)
}
return &models.PoolSwapResult{TrackingId: uid.String()}, nil
}
func (das *DevAccountService) TrackAccountStatus(ctx context.Context, publicKey string) (*models.TrackStatusResult, error) {
var ok bool
_, ok = das.accounts[publicKey]
@@ -369,22 +580,20 @@ func (das *DevAccountService) TrackAccountStatus(ctx context.Context, publicKey
func (das *DevAccountService) FetchVouchers(ctx context.Context, publicKey string) ([]dataserviceapi.TokenHoldings, error) {
var holdings []dataserviceapi.TokenHoldings
acc, ok := das.accounts[publicKey]
_, ok := das.accounts[publicKey]
if !ok {
return nil, fmt.Errorf("account not found (publickey): %v", publicKey)
}
for k, v := range(acc.Balances) {
voucher, ok := das.vouchers[k]
if !ok {
return nil, fmt.Errorf("voucher has balance but object not found: %v", k)
}
//TODO: Iterate over the account acc.Balances object
for _, voucher := range das.vouchers {
holdings = append(holdings, dataserviceapi.TokenHoldings{
ContractAddress: voucher.Address,
TokenSymbol: voucher.Symbol,
TokenDecimals: strconv.Itoa(voucher.Decimals),
Balance: strconv.Itoa(v),
TokenAddress: voucher.Address,
TokenSymbol: voucher.Symbol,
TokenDecimals: strconv.Itoa(voucher.Decimals),
Balance: strconv.Itoa(int(defaultVoucherBalance)),
})
}
return holdings, nil
}
@@ -394,24 +603,24 @@ func (das *DevAccountService) FetchTransactions(ctx context.Context, publicKey s
if !ok {
return nil, fmt.Errorf("account not found (publickey): %v", publicKey)
}
for i, v := range(acc.Txs) {
for i, v := range acc.Txs {
mytx := das.txs[v]
if i == 10 {
break
break
}
voucher, ok := das.vouchers[mytx.Voucher]
if !ok {
return nil, fmt.Errorf("voucher %s in tx list but not found in voucher list", mytx.Voucher)
}
lasttx = append(lasttx, dataserviceapi.Last10TxResponse{
Sender: mytx.From,
Recipient: mytx.To,
TransferValue: strconv.Itoa(mytx.Value),
Sender: mytx.From,
Recipient: mytx.To,
TransferValue: strconv.Itoa(mytx.Value),
ContractAddress: voucher.Address,
TxHash: mytx.Hsh,
DateBlock: mytx.When,
TokenSymbol: voucher.Symbol,
TokenDecimals: strconv.Itoa(voucher.Decimals),
TxHash: mytx.Hsh,
DateBlock: mytx.When,
TokenSymbol: voucher.Symbol,
TokenDecimals: strconv.Itoa(voucher.Decimals),
})
}
return lasttx, nil
@@ -427,22 +636,23 @@ func (das *DevAccountService) VoucherData(ctx context.Context, address string) (
return nil, fmt.Errorf("voucher address %v found but does not resolve", address)
}
return &models.VoucherDataResult{
TokenName: voucher.Name,
TokenSymbol: voucher.Symbol,
TokenDecimals: voucher.Decimals,
SinkAddress: voucher.Sink,
TokenName: voucher.Name,
TokenSymbol: voucher.Symbol,
TokenDecimals: voucher.Decimals,
SinkAddress: voucher.Sink,
TokenCommodity: voucher.Commodity,
TokenLocation: voucher.Location,
TokenLocation: voucher.Location,
}, nil
}
func (das *DevAccountService) saveTokenTransfer(ctx context.Context, mytx Tx) error {
k := "tx_" + mytx.Hsh
k := das.prefixKeyFor("tx", mytx.Hsh)
v, err := json.Marshal(mytx)
if err != nil {
return err
}
das.db.SetSession("")
das.db.SetPrefix(db.DATATYPE_USERDATA)
return das.db.Put(ctx, []byte(k), v)
}
@@ -456,12 +666,12 @@ func (das *DevAccountService) TokenTransfer(ctx context.Context, amount, from, t
}
accFrom, ok := das.accounts[from]
if !ok {
return nil, fmt.Errorf("sender account %v not found", from)
return nil, fmt.Errorf("sender account %v not found", from)
}
accTo, ok := das.accounts[to]
if !ok {
if !das.toAutoCreate {
return nil, fmt.Errorf("recipient account %v not found, and not creating", from)
return nil, fmt.Errorf("recipient account %v not found, and not creating", from)
}
}
@@ -487,13 +697,13 @@ func (das *DevAccountService) TokenTransfer(ctx context.Context, amount, from, t
}
hsh := fmt.Sprintf("0x%x", b)
mytx := Tx{
Hsh: hsh,
To: accTo.Address,
From: accFrom.Address,
Hsh: hsh,
To: accTo.Address,
From: accFrom.Address,
Voucher: voucher.Symbol,
Value: value,
Track: uid.String(),
When: time.Now(),
Value: value,
Track: uid.String(),
When: time.Now(),
}
err = das.saveTokenTransfer(ctx, mytx)
if err != nil {
@@ -502,7 +712,7 @@ func (das *DevAccountService) TokenTransfer(ctx context.Context, amount, from, t
das.txs[hsh] = mytx
if das.emitterFunc != nil {
msg := event.Msg{
Typ: event.EventTokenTransferTag,
Typ: event.EventTokenTransferTag,
Item: mytx,
}
err = das.emitterFunc(ctx, msg)
@@ -516,16 +726,184 @@ func (das *DevAccountService) TokenTransfer(ctx context.Context, amount, from, t
}, nil
}
func (das *DevAccountService) CheckAliasAddress(ctx context.Context, alias string) (*dataserviceapi.AliasAddress, error) {
func (das *DevAccountService) CheckAliasAddress(ctx context.Context, alias string) (*models.AliasAddress, error) {
addr, ok := das.accountsAlias[alias]
if !ok {
logg.ErrorCtxf(ctx, "alias check failed", "alias", alias)
return nil, fmt.Errorf("alias %s not found", alias)
}
acc, ok := das.accounts[addr]
if !ok {
logg.ErrorCtxf(ctx, "failed to resolve alias", "alias", alias)
return nil, fmt.Errorf("alias %s found but does not resolve", alias)
}
return &dataserviceapi.AliasAddress{
return &models.AliasAddress{
Address: acc.Address,
}, nil
}
func (das *DevAccountService) applyPhoneAlias(ctx context.Context, publicKey string, phoneNumber string) (bool, error) {
if phoneNumber[0] == '+' {
if !phone.IsValidPhoneNumber(phoneNumber) {
return false, fmt.Errorf("Invalid phoneNumber number: %v", phoneNumber)
}
logg.DebugCtxf(ctx, "matched phoneNumber alias", "phoneNumber", phoneNumber, "address", publicKey)
return true, nil
}
return false, nil
}
func (das *DevAccountService) RequestAlias(ctx context.Context, publicKey string, hint string) (*models.RequestAliasResult, error) {
var alias string
uid, err := uuid.NewV4()
if !aliasRegex.MatchString(hint) {
logg.ErrorCtxf(ctx, "alias hint does not match", "key", publicKey, "hint", hint)
return nil, fmt.Errorf("alias hint does not match: %s", publicKey)
}
acc, ok := das.accounts[publicKey]
if !ok {
//Handle accounts created via the api
acc = Account{
Track: uid.String(),
Address: publicKey,
}
err = das.saveAccount(ctx, acc)
if err != nil {
logg.ErrorCtxf(ctx, "account save failed with", "account", acc, "account_save_error", err)
return nil, err
}
das.accounts[publicKey] = acc
}
alias = hint
isPhone, err := das.applyPhoneAlias(ctx, publicKey, alias)
if err != nil {
logg.ErrorCtxf(ctx, "failed to apply phone alias", "public key", publicKey, "alias", alias, "error", err)
return nil, fmt.Errorf("phone parser error: %v", err)
}
if !isPhone {
for true {
addr, ok := das.accountsAlias[alias]
if !ok {
break
}
if addr == publicKey {
break
}
alias += "x"
}
acc.Alias = alias
alias = alias + searchDomain
das.accountsAlias[alias] = publicKey
err := das.saveAlias(ctx, map[string]string{alias: publicKey})
if err != nil {
logg.ErrorCtxf(ctx, "account save error", "public key", publicKey, "alias", alias, "alias_save_error", err)
return nil, fmt.Errorf("Failed to save the account alias with error: %s", err.Error())
}
}
logg.DebugCtxf(ctx, "set alias", "addr", publicKey, "alias", alias)
return &models.RequestAliasResult{
Alias: alias,
}, nil
}
func (das *DevAccountService) UpdateAlias(ctx context.Context, publicKey string, name string) (*models.RequestAliasResult, error) {
logg.DebugCtxf(ctx, "Updated the alias", "address", publicKey, "name", name)
return &models.RequestAliasResult{
Alias: name,
}, nil
}
func (das *DevAccountService) SendUpsellSMS(ctx context.Context, inviterPhone, inviteePhone string) (*models.SendSMSResponse, error) {
logg.DebugCtxf(ctx, "sent an SMS", "inviterPhone", inviterPhone, "inviteePhone", inviteePhone)
return &models.SendSMSResponse{
Invitee: inviteePhone,
}, nil
}
func (das *DevAccountService) SendPINResetSMS(ctx context.Context, admin, phone string) error {
return fmt.Errorf("unimplemented")
}
func (das *DevAccountService) SendAddressSMS(ctx context.Context, publicKey, originPhone string) error {
return fmt.Errorf("unimplemented")
}
func (das *DevAccountService) FetchTopPools(ctx context.Context) ([]dataserviceapi.PoolDetails, error) {
var topPools []dataserviceapi.PoolDetails
for _, p := range das.pools {
topPools = append(topPools, dataserviceapi.PoolDetails{
PoolName: p.Name,
PoolSymbol: p.Symbol,
PoolContractAdrress: p.Address,
})
}
return topPools, nil
}
func (das *DevAccountService) RetrievePoolDetails(ctx context.Context, sym string) (*dataserviceapi.PoolDetails, error) {
testPool := &dataserviceapi.PoolDetails{
PoolName: "DevTest",
PoolSymbol: "DEVT",
PoolContractAdrress: "0x145F87d6198dEDD45C614FFD8b70E9a2fCCc5cc9",
LimiterAddress: "",
VoucherRegistry: "",
}
return testPool, nil
}
func (das *DevAccountService) GetPoolSwappableFromVouchers(ctx context.Context, poolAddress, publicKey string) ([]dataserviceapi.TokenHoldings, error) {
var swapFromList []dataserviceapi.TokenHoldings
p, ok := das.pools[poolAddress]
if !ok {
return nil, fmt.Errorf("Invalid pool address: %v", poolAddress)
}
for _, v := range p.Vouchers {
swapFromList = append(swapFromList, dataserviceapi.TokenHoldings{
TokenAddress: v.Address,
TokenSymbol: v.Symbol,
TokenDecimals: string(defaultDecimals),
Balance: fmt.Sprintf("%f", defaultVoucherBalance),
})
}
return swapFromList, nil
}
func (das *DevAccountService) GetPoolSwappableVouchers(ctx context.Context, poolAddress string) ([]dataserviceapi.TokenHoldings, error) {
var swapToList []dataserviceapi.TokenHoldings
_, ok := das.pools[poolAddress]
if !ok {
return nil, fmt.Errorf("Invalid pool address: %v", poolAddress)
}
for _, voucher := range das.vouchers {
swapToList = append(swapToList, dataserviceapi.TokenHoldings{
TokenAddress: voucher.Address,
TokenSymbol: voucher.Symbol,
TokenDecimals: strconv.Itoa(voucher.Decimals),
})
}
return swapToList, nil
}
func (das *DevAccountService) GetSwapFromTokenMaxLimit(ctx context.Context, poolAddress, fromTokenAddress, toTokenAddress, publicKey string) (*models.MaxLimitResult, error) {
p, ok := das.pools[poolAddress]
if !ok {
return nil, fmt.Errorf("Pool address: %v not found ", poolAddress)
}
limit, ok := p.PoolLimit[fromTokenAddress]
if !ok {
return nil, fmt.Errorf("Token address: %v not found in the pool", fromTokenAddress)
}
return &models.MaxLimitResult{
Max: limit,
}, nil
}
func (das *DevAccountService) CheckTokenInPool(ctx context.Context, poolAddress, tokenAddress string) (*models.TokenInPoolResult, error) {
return &models.TokenInPoolResult{
CanSwapFrom: true,
}, nil
}

77
dev/api_test.go Normal file
View File

@@ -0,0 +1,77 @@
package dev
import (
"context"
"testing"
"git.grassecon.net/grassrootseconomics/visedriver/testutil/mocks"
)
func TestApiRequestAlias(t *testing.T) {
ctx := context.Background()
ctx = context.WithValue(ctx, "SessionId", "+25471234565")
storageService := mocks.NewMemStorageService(ctx)
svc := NewDevAccountService(ctx, storageService)
ra, err := svc.CreateAccount(ctx)
if err != nil {
t.Fatal(err)
}
addr := ra.PublicKey
_, err = svc.RequestAlias(ctx, addr, "+254f00")
if err == nil {
t.Fatalf("expected error")
}
alias := "+254712345678"
rb, err := svc.RequestAlias(ctx, addr, alias)
if err != nil {
t.Fatal(err)
}
if rb.Alias != alias {
t.Fatalf("expected '%s', got '%s'", alias, rb.Alias)
}
_, err = svc.CheckAliasAddress(ctx, alias)
if err == nil {
t.Fatalf("expected error")
}
alias = "foo"
rb, err = svc.RequestAlias(ctx, addr, alias)
if err != nil {
t.Fatal(err)
}
alias = "foo.sarafu.local"
if rb.Alias != alias {
t.Fatalf("expected '%s', got '%s'", alias, rb.Alias)
}
rc, err := svc.CheckAliasAddress(ctx, alias)
if err != nil {
t.Fatal(err)
}
if rc.Address != addr {
t.Fatalf("expected '%s', got '%s'", addr, rc.Address)
}
// create a second account
ra, err = svc.CreateAccount(ctx)
if err != nil {
t.Fatal(err)
}
addr = ra.PublicKey
alias = "foox"
rb, err = svc.RequestAlias(ctx, addr, alias)
if err != nil {
t.Fatal(err)
}
alias = "foox.sarafu.local"
if rb.Alias != alias {
t.Fatalf("expected '%s', got '%s'", alias, rb.Alias)
}
rc, err = svc.CheckAliasAddress(ctx, alias)
if err != nil {
t.Fatal(err)
}
if rc.Address != addr {
t.Fatalf("expected '%s', got '%s'", addr, rc.Address)
}
}

32
dev/data/swap_from.json Normal file
View File

@@ -0,0 +1,32 @@
{
"ok": true,
"description": "Swap from list",
"result": {
"filtered": [
{
"contractAddress": "0xc7B78Ac9ACB9E025C8234621FC515bC58179dEAe",
"tokenSymbol": "AMANI",
"tokenDecimals": "6",
"balance": ""
},
{
"contractAddress": "0xF0C3C7581b8b96B59a97daEc8Bd48247cE078674",
"tokenSymbol": "AMUA",
"tokenDecimals": "6",
"balance": ""
},
{
"contractAddress": "0x371455a30fc62736145Bd8429Fcc6481186f235F",
"tokenSymbol": "BAHARI",
"tokenDecimals": "6",
"balance": ""
},
{
"contractAddress": "0x7cA6113b59c24a880F382C7E12d609a6Eb05246b",
"tokenSymbol": "BANGLA",
"tokenDecimals": "6",
"balance": ""
}
]
}
}

14
dev/data/swap_to.json Normal file
View File

@@ -0,0 +1,14 @@
{
"ok": true,
"description": "Swap to list",
"result": {
"filtered": [
{
"contractAddress": "0x765DE816845861e75A25fCA122bb6898B8B1282a",
"tokenSymbol": "cUSD",
"tokenDecimals": "18",
"balance": ""
}
]
}
}

43
dev/data/top_pools.json Normal file
View File

@@ -0,0 +1,43 @@
{
"ok": true,
"description": "Top 5 pools sorted by swaps",
"result": {
"topPools": [
{
"poolName": "Kenya ROLA Pool",
"poolSymbol": "ROLA",
"poolContractAddress": "0x48a953cA5cf5298bc6f6Af3C608351f537AAcb9e",
"limiterAddress": "",
"voucherRegistry": ""
},
{
"poolName": "Nairobi ROLA Pool",
"poolSymbol": "NAIROBI",
"poolContractAddress": "0xB0660Ac1Ee3d32ea35bc728D7CA1705Fa5A37528",
"limiterAddress": "",
"voucherRegistry": ""
},
{
"poolName": "Friends of Kiriba Ecosystem ",
"poolSymbol": "FRIENDS",
"poolContractAddress": "0xC4848263821FA02baB2181910A2eFb9CECb2c21C",
"limiterAddress": "",
"voucherRegistry": ""
},
{
"poolName": "Resilient Community Waqfs",
"poolSymbol": "REZILIENS",
"poolContractAddress": "0x1e40951d7a28147D8B4A554C60c42766C92e2Fc6",
"limiterAddress": "",
"voucherRegistry": ""
},
{
"poolName": "GrE Tech",
"poolSymbol": "GRET",
"poolContractAddress": "0xb7B9d0A264eD1a8E2418571B7AC5933C79C9c2B8",
"limiterAddress": "",
"voucherRegistry": ""
}
]
}
}

View File

@@ -8,12 +8,12 @@ import (
const (
// TODO: integrate with sarafu-vise-events
EventTokenTransferTag = "TOKEN_TRANSFER"
EventTokenMintTag = "TOKEN_MINT"
EventRegistrationTag = "CUSTODIAL_REGISTRATION"
EventTokenMintTag = "TOKEN_MINT"
EventRegistrationTag = "CUSTODIAL_REGISTRATION"
)
type Msg struct {
Typ string
Typ string
Item any
}
@@ -26,17 +26,17 @@ type EventCustodialRegistration struct {
// fields used for handling token transfer event.
type EventTokenTransfer struct {
To string
Value int
To string
Value int
VoucherAddress string
TxHash string
From string
TxHash string
From string
}
type EventTokenMint struct {
To string
Value int
TxHash string
To string
Value int
TxHash string
VoucherAddress string
}

21
go.mod
View File

@@ -3,19 +3,34 @@ module git.grassecon.net/grassrootseconomics/sarafu-api
go 1.23.4
require (
git.defalsify.org/vise.git v0.2.3-0.20250103172917-3e190a44568d
git.grassecon.net/grassrootseconomics/visedriver v0.8.0-beta.10.0.20250113213325-5228aef0889b
git.defalsify.org/vise.git v0.2.3-0.20250120121301-10739fb4a8c9
git.grassecon.net/grassrootseconomics/common v0.0.0-20250121134736-ba8cbbccea7d
git.grassecon.net/grassrootseconomics/visedriver v0.8.0-beta.10.0.20250122123424-6749c632b0a2
github.com/gofrs/uuid v4.4.0+incompatible
github.com/grassrootseconomics/eth-custodial v1.3.0-beta
github.com/grassrootseconomics/ussd-data-service v1.2.0-beta
github.com/grassrootseconomics/ussd-data-service v1.6.0-beta
github.com/stretchr/testify v1.9.0
)
require (
github.com/barbashov/iso639-3 v0.0.0-20211020172741-1f4ffb2d8d1c // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/fxamacker/cbor/v2 v2.4.0 // indirect
github.com/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/pgx/v5 v5.7.1 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect
github.com/joho/godotenv v1.5.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/mattn/kinako v0.0.0-20170717041458-332c0a7e205a // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/rogpeppe/go-internal v1.13.1 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/x448/float16 v0.8.4 // indirect
golang.org/x/crypto v0.32.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/text v0.21.0 // indirect
gopkg.in/leonelquinteros/gotext.v1 v1.3.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

64
go.sum
View File

@@ -1,26 +1,80 @@
git.defalsify.org/vise.git v0.2.3-0.20250103172917-3e190a44568d h1:bPAOVZOX4frSGhfOdcj7kc555f8dc9DmMd2YAyC2AMw=
git.defalsify.org/vise.git v0.2.3-0.20250103172917-3e190a44568d/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck=
git.grassecon.net/grassrootseconomics/visedriver v0.8.0-beta.10.0.20250113213325-5228aef0889b h1:6SieNUSEKbkjzquuwazs/lVG56zdEWF10zQQEMRJfMs=
git.grassecon.net/grassrootseconomics/visedriver v0.8.0-beta.10.0.20250113213325-5228aef0889b/go.mod h1:E6W7ZOa7ZvVr0Bc5ot0LNSwpSPYq4hXlAIvEPy3AJ7U=
git.defalsify.org/vise.git v0.2.3-0.20250120121301-10739fb4a8c9 h1:sPcqXQcywxA8W3W+9qQncLPmsrgqTIlec7vmD4/7vyA=
git.defalsify.org/vise.git v0.2.3-0.20250120121301-10739fb4a8c9/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck=
git.grassecon.net/grassrootseconomics/common v0.0.0-20250121134736-ba8cbbccea7d h1:5mzLas+jxTUtusOKx4XvU+n2QvrV/mH17MnJRy46siQ=
git.grassecon.net/grassrootseconomics/common v0.0.0-20250121134736-ba8cbbccea7d/go.mod h1:wgQJZGIS6QuNLHqDhcsvehsbn5PvgV7aziRebMnJi60=
git.grassecon.net/grassrootseconomics/visedriver v0.8.0-beta.10.0.20250122123424-6749c632b0a2 h1:ON77G5K0JNuwPb5JT/hRfF6G6+xstlBQgEIEzWydnhg=
git.grassecon.net/grassrootseconomics/visedriver v0.8.0-beta.10.0.20250122123424-6749c632b0a2/go.mod h1:pjKp9L/ZsWW3kMB0UoIl1yv9TBIuU33mn9Aghxp7vGk=
github.com/barbashov/iso639-3 v0.0.0-20211020172741-1f4ffb2d8d1c h1:H9Nm+I7Cg/YVPpEV1RzU3Wq2pjamPc/UtHDgItcb7lE=
github.com/barbashov/iso639-3 v0.0.0-20211020172741-1f4ffb2d8d1c/go.mod h1:rGod7o6KPeJ+hyBpHfhi4v7blx9sf+QsHsA7KAsdN6U=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88=
github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/grassrootseconomics/eth-custodial v1.3.0-beta h1:twrMBhl89GqDUL9PlkzQxMP/6OST1BByrNDj+rqXDmU=
github.com/grassrootseconomics/eth-custodial v1.3.0-beta/go.mod h1:7uhRcdnJplX4t6GKCEFkbeDhhjlcaGJeJqevbcvGLZo=
github.com/grassrootseconomics/ussd-data-service v1.2.0-beta h1:fn1gwbWIwHVEBtUC2zi5OqTlfI/5gU1SMk0fgGixIXk=
github.com/grassrootseconomics/ussd-data-service v1.2.0-beta/go.mod h1:omfI0QtUwIdpu9gMcUqLMCG8O1XWjqJGBx1qUMiGWC0=
github.com/grassrootseconomics/ussd-data-service v1.4.0-beta h1:4fMd/3h2ZIhRg4GdHQmRw5FfD3MpJvFNNJQo+Q27f5M=
github.com/grassrootseconomics/ussd-data-service v1.4.0-beta/go.mod h1:9sGnorpKaK76FmOGXoh/xv7x5siSFNYdXxQo9BKW4DI=
github.com/grassrootseconomics/ussd-data-service v1.4.4-beta h1:turlyo0i3OLj29mWpWNoB/3Qao8qEngT/5d1jDWTZE4=
github.com/grassrootseconomics/ussd-data-service v1.4.4-beta/go.mod h1:9sGnorpKaK76FmOGXoh/xv7x5siSFNYdXxQo9BKW4DI=
github.com/grassrootseconomics/ussd-data-service v1.4.7-beta h1:yAe1YaOBsdxW2m20jnVU4F0kLmFr+mK/gHCWEdHmE90=
github.com/grassrootseconomics/ussd-data-service v1.4.7-beta/go.mod h1:9sGnorpKaK76FmOGXoh/xv7x5siSFNYdXxQo9BKW4DI=
github.com/grassrootseconomics/ussd-data-service v1.5.0-beta h1:BSSQL/yPEvTVku9ja/ENZyZdwZkEaiTzzOUfg72bTy4=
github.com/grassrootseconomics/ussd-data-service v1.5.0-beta/go.mod h1:9sGnorpKaK76FmOGXoh/xv7x5siSFNYdXxQo9BKW4DI=
github.com/grassrootseconomics/ussd-data-service v1.6.0-beta h1:pY6zns6ifXyClRxP+JJaWrged3oRE7tTS2xaftF9clA=
github.com/grassrootseconomics/ussd-data-service v1.6.0-beta/go.mod h1:9sGnorpKaK76FmOGXoh/xv7x5siSFNYdXxQo9BKW4DI=
github.com/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4 h1:U4kkNYryi/qfbBF8gh7Vsbuz+cVmhf5kt6pE9bYYyLo=
github.com/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4/go.mod h1:zpZDgZFzeq9s0MIeB1P50NIEWDFFHSFBohI/NbaTD/Y=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.7.1 h1:x7SYsPBYDkHDksogeSmZZ5xzThcTgRz++I5E+ePFUcs=
github.com/jackc/pgx/v5 v5.7.1/go.mod h1:e7O26IywZZ+naJtWWos6i6fvWK+29etgITqrqHLfoZA=
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mattn/kinako v0.0.0-20170717041458-332c0a7e205a h1:0Q3H0YXzMHiciXtRcM+j0jiCe8WKPQHoRgQiRTnfcLY=
github.com/mattn/kinako v0.0.0-20170717041458-332c0a7e205a/go.mod h1:CdTTBOYzS5E4mWS1N8NWP6AHI19MP0A2B18n3hLzRMk=
github.com/pashagolub/pgxmock/v4 v4.3.0 h1:DqT7fk0OCK6H0GvqtcMsLpv8cIwWqdxWgfZNLeHCb/s=
github.com/pashagolub/pgxmock/v4 v4.3.0/go.mod h1:9VoVHXwS3XR/yPtKGzwQvwZX1kzGB9sM8SviDcHDa3A=
github.com/peteole/testdata-loader v0.3.0 h1:8jckE9KcyNHgyv/VPoaljvKZE0Rqr8+dPVYH6rfNr9I=
github.com/peteole/testdata-loader v0.3.0/go.mod h1:Mt0ZbRtb56u8SLJpNP+BnQbENljMorYBpqlvt3cS83U=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/leonelquinteros/gotext.v1 v1.3.1 h1:8d9/fdTG0kn/B7NNGV1BsEyvektXFAbkMsTZS2sFSCc=
gopkg.in/leonelquinteros/gotext.v1 v1.3.1/go.mod h1:X1WlGDeAFIYsW6GjgMm4VwUwZ2XjI7Zan2InxSUQWrU=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

19
models/alias_response.go Normal file
View File

@@ -0,0 +1,19 @@
package models
type RequestAliasResult struct {
Alias string
}
type AliasAddress struct {
Address string
}
type AliasEnsResult struct {
Address string `json:"address"`
AutoChoose bool `json:"autoChoose"`
Name string `json:"name"`
}
type AliasEnsAddressResult struct {
Address string `json:"address"`
}

View File

@@ -0,0 +1,22 @@
package models
type PoolDepositResult struct {
TrackingId string `json:"trackingId"`
}
type PoolSwapQuoteResult struct {
IncludesFeesDeduction bool `json:"includesFeesDeduction"`
OutValue string `json:"outValue"`
}
type PoolSwapResult struct {
TrackingId string `json:"trackingId"`
}
type MaxLimitResult struct {
Max string `json:"max"`
}
type TokenInPoolResult struct {
CanSwapFrom bool `json:"canSwapFrom"`
}

View File

@@ -0,0 +1,5 @@
package models
type SendSMSResponse struct {
Invitee string `json:"invitee"`
}

View File

@@ -15,5 +15,19 @@ type AccountService interface {
FetchTransactions(ctx context.Context, publicKey string) ([]dataserviceapi.Last10TxResponse, error)
VoucherData(ctx context.Context, address string) (*models.VoucherDataResult, error)
TokenTransfer(ctx context.Context, amount, from, to, tokenAddress string) (*models.TokenTransferResponse, error)
CheckAliasAddress(ctx context.Context, alias string) (*dataserviceapi.AliasAddress, error)
CheckAliasAddress(ctx context.Context, alias string) (*models.AliasAddress, error)
RequestAlias(ctx context.Context, hint string, publicKey string) (*models.RequestAliasResult, error)
UpdateAlias(ctx context.Context, name string, publicKey string) (*models.RequestAliasResult, error)
SendUpsellSMS(ctx context.Context, inviterPhone, inviteePhone string) (*models.SendSMSResponse, error)
SendAddressSMS(ctx context.Context, publicKey, originPhone string) error
SendPINResetSMS(ctx context.Context, admin, phone string) error
PoolDeposit(ctx context.Context, amount, from, poolAddress, tokenAddress string) (*models.PoolDepositResult, error)
FetchTopPools(ctx context.Context) ([]dataserviceapi.PoolDetails, error)
RetrievePoolDetails(ctx context.Context, sym string) (*dataserviceapi.PoolDetails, error)
GetPoolSwappableFromVouchers(ctx context.Context, poolAddress, publicKey string) ([]dataserviceapi.TokenHoldings, error)
GetPoolSwappableVouchers(ctx context.Context, poolAddress string) ([]dataserviceapi.TokenHoldings, error)
GetPoolSwapQuote(ctx context.Context, amount, from, fromTokenAddress, poolAddress, toTokenAddress string) (*models.PoolSwapQuoteResult, error)
PoolSwap(ctx context.Context, amount, from, fromTokenAddress, poolAddress, toTokenAddress string) (*models.PoolSwapResult, error)
GetSwapFromTokenMaxLimit(ctx context.Context, poolAddress, fromTokenAddress, toTokenAddress, publicKey string) (*models.MaxLimitResult, error)
CheckTokenInPool(ctx context.Context, poolAddress, tokenAddress string) (*models.TokenInPoolResult, error)
}

View File

@@ -5,18 +5,31 @@ import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"log"
"net/http"
"net/url"
"regexp"
"strings"
"git.defalsify.org/vise.git/logging"
"git.grassecon.net/grassrootseconomics/sarafu-api/config"
"git.grassecon.net/grassrootseconomics/sarafu-api/dev"
"git.grassecon.net/grassrootseconomics/sarafu-api/models"
"git.grassecon.net/grassrootseconomics/visedriver/storage"
"github.com/grassrootseconomics/eth-custodial/pkg/api"
dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api"
)
var (
aliasRegex = regexp.MustCompile("^\\+?[a-zA-Z0-9\\-_]+$")
logg = logging.NewVanilla().WithDomain("sarafu-api.devapi")
)
type HTTPAccountService struct {
SS storage.StorageService
UseApi bool
}
// Parameters:
@@ -49,6 +62,10 @@ func (as *HTTPAccountService) TrackAccountStatus(ctx context.Context, publicKey
return &r, nil
}
func (as *HTTPAccountService) ToFqdn(alias string) string {
return alias + ".sarafu.eth"
}
// CheckBalance retrieves the balance for a given public key from the custodial balance API endpoint.
// Parameters:
// - publicKey: The public key associated with the account whose balance needs to be checked.
@@ -202,21 +219,473 @@ func (as *HTTPAccountService) TokenTransfer(ctx context.Context, amount, from, t
// CheckAliasAddress retrieves the address of an alias from the API endpoint.
// Parameters:
// - alias: The alias of the user.
func (as *HTTPAccountService) CheckAliasAddress(ctx context.Context, alias string) (*dataserviceapi.AliasAddress, error) {
var r dataserviceapi.AliasAddress
func (as *HTTPAccountService) CheckAliasAddress(ctx context.Context, alias string) (*models.AliasAddress, error) {
if as.SS == nil {
return nil, fmt.Errorf("The storage service cannot be nil")
}
logg.InfoCtxf(ctx, "resolving alias before formatting", "alias", alias)
svc := dev.NewDevAccountService(ctx, as.SS)
if as.UseApi {
logg.InfoCtxf(ctx, "resolving alias to address", "alias", alias)
return resolveAliasAddress(ctx, alias)
} else {
return svc.CheckAliasAddress(ctx, alias)
}
}
ep, err := url.JoinPath(config.CheckAliasURL, alias)
func resolveAliasAddress(ctx context.Context, alias string) (*models.AliasAddress, error) {
var aliasEnsResult models.AliasEnsAddressResult
fullURL, err := url.JoinPath(config.AliasResolverURL, alias)
if err != nil {
return nil, err
}
req, err := http.NewRequest("GET", ep, nil)
req, err := http.NewRequest("GET", fullURL, nil)
if err != nil {
return nil, err
}
_, err = doRequest(ctx, req, &aliasEnsResult)
if err != nil {
return nil, err
}
return &models.AliasAddress{Address: aliasEnsResult.Address}, nil
}
func (as *HTTPAccountService) FetchTopPools(ctx context.Context) ([]dataserviceapi.PoolDetails, error) {
svc := dev.NewDevAccountService(ctx, as.SS)
if as.UseApi {
return fetchCustodialTopPools(ctx)
} else {
return svc.FetchTopPools(ctx)
}
}
func fetchCustodialTopPools(ctx context.Context) ([]dataserviceapi.PoolDetails, error) {
var r struct {
TopPools []dataserviceapi.PoolDetails `json:"topPools"`
}
req, err := http.NewRequest("GET", config.TopPoolsURL, nil)
if err != nil {
return nil, err
}
_, err = doRequest(ctx, req, &r)
return &r, err
return r.TopPools, nil
}
func (as *HTTPAccountService) RetrievePoolDetails(ctx context.Context, sym string) (*dataserviceapi.PoolDetails, error) {
if as.UseApi {
return retrievePoolDetails(ctx, sym)
} else {
return nil, nil
}
}
func retrievePoolDetails(ctx context.Context, sym string) (*dataserviceapi.PoolDetails, error) {
var r struct {
PoolDetails dataserviceapi.PoolDetails `json:"poolDetails"`
}
ep, err := url.JoinPath(config.RetrievePoolDetailsURL, sym)
if err != nil {
return nil, err
}
req, err := http.NewRequest("GET", ep, nil)
if err != nil {
return nil, err
}
_, err = doRequest(ctx, req, &r)
if err != nil {
return nil, err
}
return &r.PoolDetails, nil
}
func (as *HTTPAccountService) PoolDeposit(ctx context.Context, amount, from, poolAddress, tokenAddress string) (*models.PoolDepositResult, error) {
var r models.PoolDepositResult
//pool deposit payload
payload := map[string]string{
"amount": amount,
"from": from,
"poolAddress": poolAddress,
"tokenAddress": tokenAddress,
}
payloadBytes, err := json.Marshal(payload)
if err != nil {
return nil, err
}
req, err := http.NewRequest("POST", config.TokenTransferURL, bytes.NewBuffer(payloadBytes))
if err != nil {
return nil, err
}
_, err = doRequest(ctx, req, &r)
if err != nil {
return nil, err
}
return &r, nil
}
func (as *HTTPAccountService) GetPoolSwapQuote(ctx context.Context, amount, from, fromTokenAddress, poolAddress, toTokenAddress string) (*models.PoolSwapQuoteResult, error) {
var r models.PoolSwapQuoteResult
//pool swap quote payload
payload := map[string]string{
"amount": amount,
"from": from,
"fromTokenAddress": fromTokenAddress,
"poolAddress": poolAddress,
"toTokenAddress": toTokenAddress,
}
payloadBytes, err := json.Marshal(payload)
if err != nil {
return nil, err
}
req, err := http.NewRequest("POST", config.PoolSwapQuoteURL, bytes.NewBuffer(payloadBytes))
if err != nil {
return nil, err
}
_, err = doRequest(ctx, req, &r)
if err != nil {
return nil, err
}
return &r, nil
}
func (as *HTTPAccountService) GetPoolSwappableFromVouchers(ctx context.Context, poolAddress, publicKey string) ([]dataserviceapi.TokenHoldings, error) {
if as.UseApi {
return as.getPoolSwappableFromVouchers(ctx, poolAddress, publicKey)
} else {
svc := dev.NewDevAccountService(ctx, as.SS)
return svc.GetPoolSwappableFromVouchers(ctx, poolAddress, publicKey)
}
}
func (as *HTTPAccountService) getPoolSwappableFromVouchers(ctx context.Context, poolAddress, publicKey string) ([]dataserviceapi.TokenHoldings, error) {
var r struct {
PoolSwappableVouchers []dataserviceapi.TokenHoldings `json:"filtered"`
}
ep, err := url.JoinPath(config.PoolSwappableVouchersURL, poolAddress, "from", publicKey)
if err != nil {
return nil, err
}
req, err := http.NewRequest("GET", ep, nil)
if err != nil {
return nil, err
}
_, err = doRequest(ctx, req, &r)
return r.PoolSwappableVouchers, nil
}
func (as *HTTPAccountService) GetPoolSwappableVouchers(ctx context.Context, poolAddress string) ([]dataserviceapi.TokenHoldings, error) {
svc := dev.NewDevAccountService(ctx, as.SS)
if as.UseApi {
return as.getPoolSwappableVouchers(ctx, poolAddress)
} else {
return svc.GetPoolSwappableVouchers(ctx, poolAddress)
}
}
func (as HTTPAccountService) getPoolSwappableVouchers(ctx context.Context, poolAddress string) ([]dataserviceapi.TokenHoldings, error) {
var r struct {
PoolSwappableVouchers []dataserviceapi.TokenHoldings `json:"filtered"`
}
basePath, err := url.JoinPath(config.PoolSwappableVouchersURL, poolAddress, "to")
if err != nil {
return nil, err
}
parsedURL, err := url.Parse(basePath)
if err != nil {
return nil, err
}
query := parsedURL.Query()
if config.IncludeStablesParam != "" {
query.Set("stables", config.IncludeStablesParam)
}
parsedURL.RawQuery = query.Encode()
req, err := http.NewRequest("GET", parsedURL.String(), nil)
if err != nil {
return nil, err
}
_, err = doRequest(ctx, req, &r)
return r.PoolSwappableVouchers, nil
}
func (as *HTTPAccountService) PoolSwap(ctx context.Context, amount, from, fromTokenAddress, poolAddress, toTokenAddress string) (*models.PoolSwapResult, error) {
var r models.PoolSwapResult
//swap payload
payload := map[string]string{
"amount": amount,
"from": from,
"fromTokenAddress": fromTokenAddress,
"poolAddress": poolAddress,
"toTokenAddress": toTokenAddress,
}
payloadBytes, err := json.Marshal(payload)
if err != nil {
return nil, err
}
req, err := http.NewRequest("POST", config.PoolSwapURL, bytes.NewBuffer(payloadBytes))
if err != nil {
return nil, err
}
_, err = doRequest(ctx, req, &r)
if err != nil {
return nil, err
}
return &r, nil
}
func (as *HTTPAccountService) GetSwapFromTokenMaxLimit(ctx context.Context, poolAddress, fromTokenAddress, toTokenAddress, publicKey string) (*models.MaxLimitResult, error) {
if as.UseApi {
return as.getSwapFromTokenMaxLimit(ctx, poolAddress, fromTokenAddress, toTokenAddress, publicKey)
} else {
svc := dev.NewDevAccountService(ctx, as.SS)
return svc.GetSwapFromTokenMaxLimit(ctx, poolAddress, fromTokenAddress, toTokenAddress, publicKey)
}
}
func (as *HTTPAccountService) getSwapFromTokenMaxLimit(ctx context.Context, poolAddress, fromTokenAddress, toTokeAddress, publicKey string) (*models.MaxLimitResult, error) {
var r models.MaxLimitResult
ep, err := url.JoinPath(config.PoolSwappableVouchersURL, poolAddress, "limit", fromTokenAddress, toTokeAddress, publicKey)
if err != nil {
return nil, err
}
req, err := http.NewRequest("GET", ep, nil)
if err != nil {
return nil, err
}
_, err = doRequest(ctx, req, &r)
if err != nil {
return nil, err
}
return &r, nil
}
func (as *HTTPAccountService) CheckTokenInPool(ctx context.Context, poolAddress, tokenAddress string) (*models.TokenInPoolResult, error) {
if as.UseApi {
return as.checkTokenInPool(ctx, poolAddress, tokenAddress)
} else {
svc := dev.NewDevAccountService(ctx, as.SS)
return svc.CheckTokenInPool(ctx, poolAddress, tokenAddress)
}
}
func (as *HTTPAccountService) checkTokenInPool(ctx context.Context, poolAddress, tokenAddress string) (*models.TokenInPoolResult, error) {
var r models.TokenInPoolResult
ep, err := url.JoinPath(config.PoolSwappableVouchersURL, poolAddress, "check", tokenAddress)
if err != nil {
return nil, err
}
req, err := http.NewRequest("GET", ep, nil)
if err != nil {
return nil, err
}
_, err = doRequest(ctx, req, &r)
if err != nil {
return nil, err
}
return &r, nil
}
// TODO: Use actual custodial api to request available alias
func (as *HTTPAccountService) RequestAlias(ctx context.Context, publicKey string, hint string) (*models.RequestAliasResult, error) {
if as.SS == nil {
return nil, fmt.Errorf("The storage service cannot be nil")
}
if as.UseApi {
if !strings.Contains(hint, ".") {
hint = as.ToFqdn(hint)
}
enr, err := requestEnsAlias(ctx, publicKey, hint)
if err != nil {
return nil, err
}
return &models.RequestAliasResult{Alias: enr.Name}, nil
} else {
svc := dev.NewDevAccountService(ctx, as.SS)
return svc.RequestAlias(ctx, publicKey, hint)
}
}
func requestEnsAlias(ctx context.Context, publicKey string, hint string) (*models.AliasEnsResult, error) {
var r models.AliasEnsResult
endpoint := config.AliasRegistrationURL
logg.InfoCtxf(ctx, "requesting alias", "endpoint", endpoint, "hint", hint)
//Payload with the address and hint to derive an ENS name
payload := map[string]string{
"address": publicKey,
"hint": hint,
}
payloadBytes, err := json.Marshal(payload)
if err != nil {
return nil, err
}
req, err := http.NewRequest("POST", endpoint, bytes.NewBuffer(payloadBytes))
if err != nil {
return nil, err
}
// Log the request body
logg.InfoCtxf(ctx, "request body", "payload", string(payloadBytes))
_, err = doRequest(ctx, req, &r)
if err != nil {
return nil, err
}
logg.InfoCtxf(ctx, "alias successfully assigned", "alias", r.Name)
return &r, nil
}
func (as *HTTPAccountService) UpdateAlias(ctx context.Context, name string, publicKey string) (*models.RequestAliasResult, error) {
if as.SS == nil {
return nil, fmt.Errorf("The storage service cannot be nil")
}
if as.UseApi {
if !strings.Contains(name, ".") {
name = as.ToFqdn(name)
}
enr, err := updateEnsAlias(ctx, name, publicKey)
if err != nil {
return nil, err
}
return &models.RequestAliasResult{Alias: enr.Name}, nil
} else {
svc := dev.NewDevAccountService(ctx, as.SS)
return svc.RequestAlias(ctx, publicKey, name)
}
}
func updateEnsAlias(ctx context.Context, name string, publicKey string) (*models.AliasEnsResult, error) {
var r models.AliasEnsResult
endpoint := config.AliasUpdateURL
logg.InfoCtxf(ctx, "updating alias", "endpoint", endpoint, "name", name)
payload := map[string]string{
"name": name,
"address": publicKey,
}
payloadBytes, err := json.Marshal(payload)
if err != nil {
return nil, err
}
req, err := http.NewRequest("PUT", endpoint, bytes.NewBuffer(payloadBytes))
if err != nil {
return nil, err
}
// Log the request body
logg.InfoCtxf(ctx, "request body", "payload", string(payloadBytes))
_, err = doRequest(ctx, req, &r)
if err != nil {
return nil, err
}
logg.InfoCtxf(ctx, "alias successfully updated", "alias", r.Name)
return &r, nil
}
// SendSMS calls the API to send out an SMS.
// Parameters:
// - inviterPhone: The user initiating the SMS.
// - inviteePhone: The number being invited to Sarafu.
func (as *HTTPAccountService) SendUpsellSMS(ctx context.Context, inviterPhone, inviteePhone string) (*models.SendSMSResponse, error) {
var r models.SendSMSResponse
// Create request payload
payload := map[string]string{
"inviterPhone": inviterPhone,
"inviteePhone": inviteePhone,
}
payloadBytes, err := json.Marshal(payload)
if err != nil {
return nil, err
}
// Create a new request
req, err := http.NewRequest("POST", config.SendSMSURL, bytes.NewBuffer(payloadBytes))
if err != nil {
return nil, err
}
_, err = doRequest(ctx, req, &r)
if err != nil {
return nil, err
}
return &r, nil
}
func (as *HTTPAccountService) SendAddressSMS(ctx context.Context, publicKey, originPhone string) error {
ep, err := url.JoinPath(config.ExternalSMSURL, "address")
if err != nil {
return err
}
logg.InfoCtxf(ctx, "sending an address sms", "endpoint", ep, "address", publicKey, "origin-phone", originPhone)
payload := map[string]string{
"address": publicKey,
"originPhone": originPhone,
}
payloadBytes, err := json.Marshal(payload)
if err != nil {
return err
}
req, err := http.NewRequest("POST", ep, bytes.NewBuffer(payloadBytes))
if err != nil {
return err
}
_, err = doRequest(ctx, req, nil)
if err != nil {
return err
}
return nil
}
func (as *HTTPAccountService) SendPINResetSMS(ctx context.Context, admin, phone string) error {
ep, err := url.JoinPath(config.ExternalSMSURL, "pinreset")
if err != nil {
return err
}
logg.InfoCtxf(ctx, "sending pin reset sms", "endpoint", ep, "admin", admin, "phone", phone)
payload := map[string]string{
"admin": admin,
"phone": phone,
}
payloadBytes, err := json.Marshal(payload)
if err != nil {
return err
}
req, err := http.NewRequest("POST", ep, bytes.NewBuffer(payloadBytes))
if err != nil {
return err
}
_, err = doRequest(ctx, req, nil)
if err != nil {
return err
}
return nil
}
// TODO: remove eth-custodial api dependency
@@ -227,6 +696,7 @@ func doRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKRespons
req.Header.Set("Authorization", "Bearer "+config.BearerToken)
req.Header.Set("Content-Type", "application/json")
// Log request
logRequestDetails(req)
resp, err := http.DefaultClient.Do(req)
@@ -237,22 +707,26 @@ func doRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKRespons
}
defer resp.Body.Close()
log.Printf("Received response for %s: Status Code: %d | Content-Type: %s", req.URL, resp.StatusCode, resp.Header.Get("Content-Type"))
// Read and log response body
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
log.Printf("Received response for %s: Status Code: %d | Content-Type: %s | Body: %s",
req.URL, resp.StatusCode, resp.Header.Get("Content-Type"), string(body))
if resp.StatusCode >= http.StatusBadRequest {
err := json.Unmarshal([]byte(body), &errResponse)
if err != nil {
if err := json.Unmarshal(body, &errResponse); err != nil {
return nil, err
}
return nil, errors.New(errResponse.Description)
}
err = json.Unmarshal([]byte(body), &okResponse)
if err != nil {
if err := json.Unmarshal(body, &okResponse); err != nil {
return nil, err
}
if len(okResponse.Result) == 0 {
return nil, errors.New("Empty api result")
}
@@ -269,16 +743,14 @@ func doRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKRespons
func logRequestDetails(req *http.Request) {
var bodyBytes []byte
contentType := req.Header.Get("Content-Type")
if req.Body != nil {
bodyBytes, err := io.ReadAll(req.Body)
if err != nil {
log.Printf("Error reading request body: %s", err)
return
}
req.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
bodyBytes, _ = io.ReadAll(req.Body)
req.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) // Restore body
} else {
bodyBytes = []byte("-")
}
log.Printf("URL: %s | Content-Type: %s | Method: %s| Request Body: %s", req.URL, contentType, req.Method, string(bodyBytes))
log.Printf("Outgoing Request -> URL: %s | Method: %s | Content-Type: %s | Body: %s",
req.URL.String(), req.Method, contentType, string(bodyBytes))
}

View File

@@ -0,0 +1,71 @@
package mocks
import (
"context"
"git.defalsify.org/vise.git/logging"
"git.grassecon.net/grassrootseconomics/sarafu-api/models"
dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api"
)
var (
logg = logging.NewVanilla().WithDomain("sarafu-vise-events.testutil")
)
const (
AliceChecksum = "0xeae046BF396e91f5A8D74f863dC57c107c8a4a70"
BobChecksum = "0xB3117202371853e24B725d4169D87616A7dDb127"
AliceSession = "5553425"
)
type MockApi struct {
TransactionsContent []dataserviceapi.Last10TxResponse
VouchersContent []dataserviceapi.TokenHoldings
VoucherDataContent *models.VoucherDataResult
}
func (m MockApi) CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResult, error) {
return nil, nil
}
func (m MockApi) CreateAccount(ctx context.Context) (*models.AccountResult, error) {
return nil, nil
}
func (m MockApi) TrackAccountStatus(ctx context.Context, publicKey string) (*models.TrackStatusResult, error) {
return nil, nil
}
func (m MockApi) FetchVouchers(ctx context.Context, publicKey string) ([]dataserviceapi.TokenHoldings, error) {
logg.DebugCtxf(ctx, "mockapi fetchvouchers", "key", publicKey)
return m.VouchersContent, nil
}
func (m MockApi) FetchTransactions(ctx context.Context, publicKey string) ([]dataserviceapi.Last10TxResponse, error) {
logg.DebugCtxf(ctx, "mockapi fetchtransactions", "key", publicKey)
return m.TransactionsContent, nil
}
func (m MockApi) VoucherData(ctx context.Context, address string) (*models.VoucherDataResult, error) {
return m.VoucherDataContent, nil
}
func (m MockApi) CheckAliasAddress(ctx context.Context, alias string) (*models.AliasAddress, error) {
return nil, nil
}
func (m MockApi) RequestAlias(ctx context.Context, publicKey string, hint string) (*models.RequestAliasResult, error) {
return nil, nil
}
func (m MockApi) UpdateAlias(ctx context.Context, publicKey string, name string) (*models.RequestAliasResult, error) {
return nil, nil
}
func (m MockApi) TokenTransfer(ctx context.Context, amount, from, to, tokenAddress string) (*models.TokenTransferResponse, error) {
return nil, nil
}
func (m MockApi) SendUpsellSMS(ctx context.Context, inviterPhone, inviteePhone string) (*models.SendSMSResponse, error) {
return nil, nil
}

View File

@@ -48,7 +48,75 @@ func (m *MockAccountService) TokenTransfer(ctx context.Context, amount, from, to
return args.Get(0).(*models.TokenTransferResponse), args.Error(1)
}
func (m *MockAccountService) CheckAliasAddress(ctx context.Context, alias string) (*dataserviceapi.AliasAddress, error) {
func (m *MockAccountService) CheckAliasAddress(ctx context.Context, alias string) (*models.AliasAddress, error) {
args := m.Called(alias)
return args.Get(0).(*dataserviceapi.AliasAddress), args.Error(1)
return args.Get(0).(*models.AliasAddress), args.Error(1)
}
func (m *MockAccountService) RequestAlias(ctx context.Context, publicKey string, hint string) (*models.RequestAliasResult, error) {
args := m.Called(publicKey, hint)
return args.Get(0).(*models.RequestAliasResult), args.Error(1)
}
func (m *MockAccountService) UpdateAlias(ctx context.Context, publicKey string, name string) (*models.RequestAliasResult, error) {
args := m.Called(publicKey, name)
return args.Get(0).(*models.RequestAliasResult), args.Error(1)
}
func (m *MockAccountService) SendUpsellSMS(ctx context.Context, inviterPhone, inviteePhone string) (*models.SendSMSResponse, error) {
args := m.Called(inviterPhone, inviteePhone)
return args.Get(0).(*models.SendSMSResponse), args.Error(1)
}
func (m *MockAccountService) SendPINResetSMS(ctx context.Context, admin, phone string) error {
return nil
}
func (m *MockAccountService) SendAddressSMS(ctx context.Context, publicKey, originPhone string) error {
return nil
}
func (m MockAccountService) PoolDeposit(ctx context.Context, amount, from, poolAddress, tokenAddress string) (*models.PoolDepositResult, error) {
args := m.Called(amount, from, poolAddress, tokenAddress)
return args.Get(0).(*models.PoolDepositResult), args.Error(1)
}
func (m MockAccountService) FetchTopPools(ctx context.Context) ([]dataserviceapi.PoolDetails, error) {
args := m.Called()
return args.Get(0).([]dataserviceapi.PoolDetails), args.Error(1)
}
func (m MockAccountService) RetrievePoolDetails(ctx context.Context, sym string) (*dataserviceapi.PoolDetails, error) {
args := m.Called()
return args.Get(0).(*dataserviceapi.PoolDetails), args.Error(1)
}
func (m MockAccountService) GetPoolSwappableFromVouchers(ctx context.Context, poolAddress, publicKey string) ([]dataserviceapi.TokenHoldings, error) {
args := m.Called(poolAddress, publicKey)
return args.Get(0).([]dataserviceapi.TokenHoldings), args.Error(1)
}
func (m MockAccountService) GetPoolSwappableVouchers(ctx context.Context, poolAddress string) ([]dataserviceapi.TokenHoldings, error) {
args := m.Called(poolAddress)
return args.Get(0).([]dataserviceapi.TokenHoldings), args.Error(1)
}
func (m MockAccountService) GetPoolSwapQuote(ctx context.Context, amount, from, fromTokenAddress, poolAddress, toTokenAddress string) (*models.PoolSwapQuoteResult, error) {
args := m.Called(amount, from, fromTokenAddress, poolAddress, toTokenAddress)
return args.Get(0).(*models.PoolSwapQuoteResult), args.Error(1)
}
func (m MockAccountService) PoolSwap(ctx context.Context, amount, from, fromTokenAddress, poolAddress, toTokenAddress string) (*models.PoolSwapResult, error) {
args := m.Called(amount, from, fromTokenAddress, poolAddress, toTokenAddress)
return args.Get(0).(*models.PoolSwapResult), args.Error(1)
}
func (m MockAccountService) GetSwapFromTokenMaxLimit(ctx context.Context, poolAddress, fromTokenAddress, toTokenAddress, publicKey string) (*models.MaxLimitResult, error) {
args := m.Called(poolAddress, fromTokenAddress, toTokenAddress, publicKey)
return args.Get(0).(*models.MaxLimitResult), args.Error(1)
}
func (m MockAccountService) CheckTokenInPool(ctx context.Context, poolAddress, tokenAddress string) (*models.TokenInPoolResult, error) {
args := m.Called(poolAddress, tokenAddress)
return args.Get(0).(*models.TokenInPoolResult), args.Error(1)
}

View File

@@ -35,10 +35,10 @@ func (tas *TestAccountService) TrackAccountStatus(ctx context.Context, publicKey
func (tas *TestAccountService) FetchVouchers(ctx context.Context, publicKey string) ([]dataserviceapi.TokenHoldings, error) {
return []dataserviceapi.TokenHoldings{
dataserviceapi.TokenHoldings{
ContractAddress: "0x6CC75A06ac72eB4Db2eE22F781F5D100d8ec03ee",
TokenSymbol: "SRF",
TokenDecimals: "6",
Balance: "2745987",
TokenAddress: "0x6CC75A06ac72eB4Db2eE22F781F5D100d8ec03ee",
TokenSymbol: "SRF",
TokenDecimals: "6",
Balance: "2745987",
},
}, nil
}
@@ -57,6 +57,62 @@ func (tas *TestAccountService) TokenTransfer(ctx context.Context, amount, from,
}, nil
}
func (m TestAccountService) CheckAliasAddress(ctx context.Context, alias string) (*dataserviceapi.AliasAddress, error) {
return &dataserviceapi.AliasAddress{}, nil
func (m TestAccountService) PoolDeposit(ctx context.Context, amount, from, poolAddress, tokenAddress string) (*models.PoolDepositResult, error) {
return &models.PoolDepositResult{}, nil
}
func (m *TestAccountService) CheckAliasAddress(ctx context.Context, alias string) (*models.AliasAddress, error) {
return &models.AliasAddress{}, nil
}
func (m *TestAccountService) RequestAlias(ctx context.Context, publicKey string, hint string) (*models.RequestAliasResult, error) {
return &models.RequestAliasResult{}, nil
}
func (m *TestAccountService) UpdateAlias(ctx context.Context, publicKey string, hint string) (*models.RequestAliasResult, error) {
return &models.RequestAliasResult{}, nil
}
func (m *TestAccountService) SendUpsellSMS(ctx context.Context, inviterPhone, inviteePhone string) (*models.SendSMSResponse, error) {
return &models.SendSMSResponse{}, nil
}
func (m *TestAccountService) SendAddressSMS(ctx context.Context, publicKey, originPhone string) error {
return nil
}
func (m *TestAccountService) SendPINResetSMS(ctx context.Context, admin, phone string) error {
return nil
}
func (m TestAccountService) FetchTopPools(ctx context.Context) ([]dataserviceapi.PoolDetails, error) {
return []dataserviceapi.PoolDetails{}, nil
}
func (m TestAccountService) RetrievePoolDetails(ctx context.Context, sym string) (*dataserviceapi.PoolDetails, error) {
return &dataserviceapi.PoolDetails{}, nil
}
func (m TestAccountService) GetPoolSwappableFromVouchers(ctx context.Context, poolAddress, publicKey string) ([]dataserviceapi.TokenHoldings, error) {
return []dataserviceapi.TokenHoldings{}, nil
}
func (m TestAccountService) GetPoolSwappableVouchers(ctx context.Context, poolAddress string) ([]dataserviceapi.TokenHoldings, error) {
return []dataserviceapi.TokenHoldings{}, nil
}
func (m TestAccountService) GetPoolSwapQuote(ctx context.Context, amount, from, fromTokenAddress, poolAddress, toTokenAddress string) (*models.PoolSwapQuoteResult, error) {
return &models.PoolSwapQuoteResult{}, nil
}
func (m TestAccountService) PoolSwap(ctx context.Context, amount, from, fromTokenAddress, poolAddress, toTokenAddress string) (*models.PoolSwapResult, error) {
return &models.PoolSwapResult{}, nil
}
func (m TestAccountService) GetSwapFromTokenMaxLimit(ctx context.Context, poolAddress, fromTokenAddress, toTokenAddress, publicKey string) (*models.MaxLimitResult, error) {
return &models.MaxLimitResult{}, nil
}
func (m TestAccountService) CheckTokenInPool(ctx context.Context, poolAddress, tokenAddress string) (*models.TokenInPoolResult, error) {
return &models.TokenInPoolResult{}, nil
}