Documentation
¶
Overview ¶
yuri is a relatively self contained cryptocurrency invoice library.
we handle creating invoices, polling known cryptocurrencies (via CryptoProvider) users are expected to bring their own Storage implementation
This project is licensed under the GNU AGPLv3 accessible in the License file at the root of the package.
Example ¶
ctx := context.Background()
instance, err := New(Options{
Hooks: Hooks{
OnError: func(err error) {
// if an error happened during Polling/Run itll be given here
// instead of nuking ur app. kill the run Context if you want.
// do you.
},
OnInvoiceUpdated: func(ctx context.Context, i Invoice) error {
// an invoice updated
if i.Paid() {
// they paid, its confirmed. do thing.
return nil
}
return nil
},
},
Chains: []CryptoProvider{
NewMonero(JsonRpcClientConfig{
Host: "",
Username: "",
Password: "",
// Client: override or uses http.DefaultClient,
}),
},
Pricing: []PriceProvider{
// these are the same, by default a CachedPriceProvider lives in memory,
// if you wish to coordinate/cache the pricing between restarts you can wrap
// [PricingProvider] with Redis etc.
NewCachedPriceProvider(NewCoinGeckoPriceProvider(nil)),
NewCachedPriceProviderWithTTL(NewCoinGeckoPriceProvider(nil), 3*time.Minute),
},
Storage: &InMemoryStorage{},
PollEvery: 15 * time.Second,
MaxPollDuration: 15 * time.Second,
})
if err != nil {
// you fucked up an option
return
}
go instance.Run(ctx)
inv, err := instance.NewInvoice(ctx, InvoiceCreate{
Chain: Monero,
AmountFiat: USD.Of(10.50),
})
if err != nil {
// do something
return
}
log.Printf("addr = %s owed = %s", inv.Address, inv.AmountOwed.String())
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
<-c
Index ¶
- Constants
- Variables
- func NewBNB(rpcConf JsonRpcClientConfig) ethereumLike
- func NewBitcoin(rpcConf JsonRpcClientConfig) bitcoinLike
- func NewEthereum(rpcConf JsonRpcClientConfig) ethereumLike
- func NewEthereumLike(chain, symbol string, rpcConf JsonRpcClientConfig) ethereumLike
- func NewLitecoin(rpcConf JsonRpcClientConfig) bitcoinLike
- func NewMonero(rpcConf JsonRpcClientConfig) monero
- func NewSolana(opts SolanaOptions) solanaProvider
- func RPCDo(ctx context.Context, client JsonRpcClient, req JsonRpcRequest, out any) error
- type Chain
- type CryptoProvider
- type Currency
- type Hooks
- type InMemoryStorage
- type Instance
- type Invoice
- type InvoiceCreate
- type JsonRpcClient
- type JsonRpcClientConfig
- type JsonRpcRequest
- type JsonRpcResponse
- type Options
- type PriceProvider
- func NewBareBitcoinPriceProvider(client *http.Client) PriceProvider
- func NewBitbankPriceProvider(client *http.Client) PriceProvider
- func NewBitcoinKenyaPriceProvider(client *http.Client) PriceProvider
- func NewBitflyerPriceProvider(client *http.Client) PriceProvider
- func NewBitmyntPriceProvider(client *http.Client) PriceProvider
- func NewBitnobPriceProvider(client *http.Client) PriceProvider
- func NewBitpayPriceProvider(client *http.Client) PriceProvider
- func NewBtcTurkPriceProvider(client *http.Client) PriceProvider
- func NewBudaPriceProvider(client *http.Client) PriceProvider
- func NewByllsPriceProvider(client *http.Client) PriceProvider
- func NewCachedPriceProvider(inner PriceProvider) PriceProvider
- func NewCachedPriceProviderWithTTL(inner PriceProvider, ttl time.Duration) PriceProvider
- func NewCoinDCXPriceProvider(client *http.Client) PriceProvider
- func NewCoinGeckoPriceProvider(client *http.Client) PriceProvider
- func NewCoinmatePriceProvider(client *http.Client) PriceProvider
- func NewCryptoMarketPriceProvider(client *http.Client) PriceProvider
- func NewDesiboardPriceProvider(client *http.Client) PriceProvider
- func NewFreeCurrencyRatesPriceProvider(client *http.Client) PriceProvider
- func NewHitBTCPriceProvider(client *http.Client) PriceProvider
- func NewKrakenPriceProvider(client *http.Client) PriceProvider
- func NewLunoPriceProvider(client *http.Client) PriceProvider
- func NewNullPriceProvider() PriceProvider
- func NewRipioPriceProvider(client *http.Client) PriceProvider
- func NewYadioPriceProvider(client *http.Client) PriceProvider
- type PricingSymbolProvider
- type SolanaHooks
- type SolanaOptions
- type Storage
- type Token
Examples ¶
Constants ¶
const ( JsonRPCParseError = -32700 JsonRPCInvalidRequestError = -32600 JsonRPCMethodNotFoundError = -32601 JsonRPCInvalidParamsError = -32602 JsonRPCInternalErrorError = -32603 )
const DefaultPriceCacheTTL = 3 * time.Minute
Variables ¶
var ( FiatCurrencyNotSupportedErr = errors.New("requested currency is not supported by the pricing provider") ChainNotSupportedErr = errors.New("requested chain/token is not supported by the pricing provider") )
var ( USD = Currency{Code: "USD", Decimals: 2} EUR = Currency{Code: "EUR", Decimals: 2} GBP = Currency{Code: "GBP", Decimals: 2} JPY = Currency{Code: "JPY", Decimals: 0} )
Functions ¶
func NewBNB ¶ added in v0.0.5
func NewBNB(rpcConf JsonRpcClientConfig) ethereumLike
NewBNB constructs a new ethereumLike CryptoProvider preconfigured for the BNB chain.
func NewBitcoin ¶
func NewBitcoin(rpcConf JsonRpcClientConfig) bitcoinLike
func NewEthereum ¶
func NewEthereum(rpcConf JsonRpcClientConfig) ethereumLike
NewEthereum constructs a new ethereumLike CryptoProvider preconfigured for the standard Eth chain.
func NewEthereumLike ¶ added in v0.0.5
func NewEthereumLike(chain, symbol string, rpcConf JsonRpcClientConfig) ethereumLike
NewEthereumLike constructs a new ethereumLike CryptoProvider for generic EVM compatible chains. For example Eth(base), BNB, Eth(eth).
Chain is the name of the chain, this must be unique. Symbol is the pricing symbol, for example "ETH" for Eth(eth).
func NewLitecoin ¶
func NewLitecoin(rpcConf JsonRpcClientConfig) bitcoinLike
func NewMonero ¶
func NewMonero(rpcConf JsonRpcClientConfig) monero
NewMonero creates a new CryptoProvider for Monero/XMR
This base implementation always uses account index 0 (zero)
func NewSolana ¶ added in v0.0.8
func NewSolana(opts SolanaOptions) solanaProvider
TODO: maybe stop people from being dumb and assuming that they don't have to pass in the hooks. something like that. but until this is v1000.00.05 i don't care! RTFM!
func RPCDo ¶
func RPCDo( ctx context.Context, client JsonRpcClient, req JsonRpcRequest, out any, ) error
Types ¶
type Chain ¶
type Chain string
Chain is the full name of the chain in lowercase. (e.g. monero)
const BNB Chain = "bnb"
const Bitcoin Chain = "bitcoin"
const Ethereum Chain = "ethereum"
const Litecoin Chain = "litecoin"
const Monero Chain = "monero"
const Solana Chain = "solana"
type CryptoProvider ¶
type CryptoProvider interface {
// Chain is the full name of the chain in lowercase. (e.g. monero)
Chain() Chain
// CreateAddress creates a new subaddress/derived address from the root address
// provided at init time. Each provider should handle if they want to manually
// derive it, or request it from something like a JSON-RPC server.
CreateAddress(context.Context) (string, error)
// Decimals gets the amount of decimals for the Native currency of this provider
Decimals() int64
// Poll gets called with the Invoices with a matching [Chain].
// It is expected to return the updated values for each invoice.
//
// You are expected to return a new [Invoice] with updated balances,
// or anything else of relevance. You are expected to copy over metadata
// between the invoices.
//
// Please view [Invoice]'s comments if you are implementing this.
Poll(context.Context, []Invoice) ([]Invoice, error)
}
CryptoProvider standardizes how cryptocurrency providers (e.g. monero) should handle core functionality.
Every method implemented by a CryptoProvider must support being called concurrently.
type Currency ¶
type Hooks ¶
type Hooks struct {
// If [Storage.UpdateInvoices] fails this method is not called for the Invoice(s)
OnInvoiceUpdated func(context.Context, Invoice) error
OnError func(error)
}
Hooks are lifecycle state to that you don't need to handle lifecycle updates in Storage handlers.
type InMemoryStorage ¶
type InMemoryStorage struct {
// contains filtered or unexported fields
}
InMemoryStorage implements Storage in process memory, this should be used for exclusively testing, as it does not attempt to persist and heavily abuses mutexes.
func (*InMemoryStorage) GetActiveInvoices ¶
GetActiveInvoices implements Storage.
func (*InMemoryStorage) NewInvoice ¶
func (i *InMemoryStorage) NewInvoice(_ context.Context, inv Invoice) error
NewInvoice implements Storage.
func (*InMemoryStorage) UpdateInvoices ¶
func (i *InMemoryStorage) UpdateInvoices(_ context.Context, invoices []Invoice) error
UpdateInvoices implements Storage.
type Instance ¶
type Instance struct {
// contains filtered or unexported fields
}
Instance represents a Yuri invoice instance, which is used to manage [CryptoProvider]s. Instance manages creation of invoices, alongside emitting event updates to you, the end user, using Hooks
func (*Instance) NewInvoice ¶
NewInvoice creates a new invoice with the Instance using the respective CryptoProvider and PriceProvider
type Invoice ¶
type Invoice struct {
Chain Chain `json:"chain"`
Address string `json:"address"`
AmountOwed *big.Int `json:"amount_owed"`
AmountPaid *big.Int `json:"amount_paid"`
Token Token `json:"token"`
// if the amountPaid amount has not reached the required confirmations
Pending bool `json:"pending"`
// user provided metadata, typically an ID to allow for
// quickly locating and updating the Invoice via [Storage.UpdateInvoice]
Metadata map[string]any `json:"metadata"`
}
Invoice represents the smallest state an invoice can be. You are required to persist the following: Invoice.Chain, Invoice.Address, [Invoice.AmountOwed, Invoice.AmountPaid, Invoice.Token
func (Invoice) Paid ¶
Paid determines if an Invoice is fully paid, and accounts for pending funds.
AmountPaid >= AmountOwed, but one or more contributing transfers have not reached the required confirmation threshold.
NOTE: depending on the CryptoProvider's implementation this may vary. Some implementations are naive and assume if there is any pending transaction to the addr that is confirming, even if the addr is >=AmountOwed in balance then the entire balance is pending.
This is behaviour you should expect!
type InvoiceCreate ¶
type JsonRpcClient ¶
type JsonRpcClient struct {
// contains filtered or unexported fields
}
JsonRpcClient is a small JSON-RPC 2.0 client this client does not support batching.
Impls https://www.simple-is-better.org/json-rpc/transport_http.html
func NewJsonRpcClient ¶
func NewJsonRpcClient(conf JsonRpcClientConfig) JsonRpcClient
func (JsonRpcClient) Do ¶
func (c JsonRpcClient) Do(ctx context.Context, request JsonRpcRequest) (JsonRpcResponse, error)
You generally want to use RPCDo instead for an easier life.
type JsonRpcClientConfig ¶
type JsonRpcRequest ¶
type JsonRpcRequest struct {
// Must be exactly 2.0
JsonRPC string `json:"jsonrpc"`
Method string `json:"method"`
// NOTE: previously we only supported maps/actual objects,
// but uhhh.. nope! fuck you. Bitcoin forced my hand
// because they dont accept named args.
Params any `json:"params,omitempty"`
// if ID is omitted this request is a Notification
Id string `json:"id,omitempty"`
}
type JsonRpcResponse ¶
type JsonRpcResponse struct {
// Must be exactly 2.0
JsonRPC string `json:"jsonrpc"`
// mutually exclusive with Error
Result json.RawMessage `json:"result"`
// mutually exclusive with Result
Error jsonRpcError `json:"error"`
// can be nullable if there was no id in
// the request object
Id string `json:"id,omitempty"`
}
type Options ¶
type Options struct {
Chains []CryptoProvider
Pricing []PriceProvider
Hooks Hooks
Storage Storage
// How often we should poll for new changes on each provided
// CryptoProvider. The default is 15 seconds.
PollEvery time.Duration
// MaxPollDuration is the maximum amount of time a CryptoProvider
// is allowed to Poll for. (how long each crypto can Poll for)
MaxPollDuration time.Duration
}
type PriceProvider ¶
type PriceProvider interface {
WantsFullChainName() bool
// Get fetches the price of a cryptocurrency(/token) from a remote
// pricing provider, for example CoinGecko, in the requested currency.
//
// Get MUST ALWAYS be safe to call concurrently.
//
// The return value should be in the minor fiat unit, for example
// cents.
//
// If the provider does not support the requested fiat currency
// then CurrencyNotSupportedErr should be thrown.
//
// If the provider does not support the requested chain/token
// then ChainNotSupportedErr should be thrown.
Get(ctx context.Context, currency Currency, chain string, token Token) (fiatMinorUnits int64, err error)
}
PriceProvider respresents some place where we can fetch current prices for a cryptocurrency. A PriceProvider MUST ALWAYS be safe to call concurrently.
func NewBareBitcoinPriceProvider ¶
func NewBareBitcoinPriceProvider(client *http.Client) PriceProvider
func NewBitbankPriceProvider ¶
func NewBitbankPriceProvider(client *http.Client) PriceProvider
func NewBitcoinKenyaPriceProvider ¶
func NewBitcoinKenyaPriceProvider(client *http.Client) PriceProvider
func NewBitflyerPriceProvider ¶
func NewBitflyerPriceProvider(client *http.Client) PriceProvider
func NewBitmyntPriceProvider ¶
func NewBitmyntPriceProvider(client *http.Client) PriceProvider
func NewBitnobPriceProvider ¶
func NewBitnobPriceProvider(client *http.Client) PriceProvider
func NewBitpayPriceProvider ¶
func NewBitpayPriceProvider(client *http.Client) PriceProvider
func NewBtcTurkPriceProvider ¶
func NewBtcTurkPriceProvider(client *http.Client) PriceProvider
func NewBudaPriceProvider ¶
func NewBudaPriceProvider(client *http.Client) PriceProvider
func NewByllsPriceProvider ¶
func NewByllsPriceProvider(client *http.Client) PriceProvider
func NewCachedPriceProvider ¶
func NewCachedPriceProvider(inner PriceProvider) PriceProvider
func NewCachedPriceProviderWithTTL ¶
func NewCachedPriceProviderWithTTL(inner PriceProvider, ttl time.Duration) PriceProvider
func NewCoinDCXPriceProvider ¶
func NewCoinDCXPriceProvider(client *http.Client) PriceProvider
func NewCoinGeckoPriceProvider ¶
func NewCoinGeckoPriceProvider(client *http.Client) PriceProvider
func NewCoinmatePriceProvider ¶
func NewCoinmatePriceProvider(client *http.Client) PriceProvider
func NewCryptoMarketPriceProvider ¶
func NewCryptoMarketPriceProvider(client *http.Client) PriceProvider
func NewDesiboardPriceProvider ¶
func NewDesiboardPriceProvider(client *http.Client) PriceProvider
func NewFreeCurrencyRatesPriceProvider ¶
func NewFreeCurrencyRatesPriceProvider(client *http.Client) PriceProvider
func NewHitBTCPriceProvider ¶
func NewHitBTCPriceProvider(client *http.Client) PriceProvider
func NewKrakenPriceProvider ¶
func NewKrakenPriceProvider(client *http.Client) PriceProvider
func NewLunoPriceProvider ¶
func NewLunoPriceProvider(client *http.Client) PriceProvider
func NewNullPriceProvider ¶
func NewNullPriceProvider() PriceProvider
func NewRipioPriceProvider ¶
func NewRipioPriceProvider(client *http.Client) PriceProvider
func NewYadioPriceProvider ¶
func NewYadioPriceProvider(client *http.Client) PriceProvider
type PricingSymbolProvider ¶
type PricingSymbolProvider interface {
PriceSymbol() string
}
PricingSymbolProvider can be implemented by CryptoProvider implementations that need to expose a different market symbol than their Chain name. This is optional and exists so pricing providers can avoid hardcoded symbol maps.
type SolanaHooks ¶ added in v0.0.8
type SolanaHooks struct {
// OnNewAddress is called when NewAddress is called, you are expected
// to manage your own storage solution for your wallets.
// Returning an error from this method results in the address not being generated.
OnNewAddress func(context.Context, ed25519.PublicKey, ed25519.PrivateKey) error
}
SolanaHooks allows the end user to deal with how they want to manage their custodial wallets
type SolanaOptions ¶ added in v0.0.8
type SolanaOptions struct {
// Hooks directly manage storing the generated wallets, it is on you
// to store your wallets in a safe place... atleast if you want to get
// your funds out of the invoice wallets. heh..
Hooks SolanaHooks
Rpc JsonRpcClientConfig
// contains filtered or unexported fields
}
type Storage ¶
type Storage interface {
GetActiveInvoices(context.Context, Chain) ([]Invoice, error)
UpdateInvoices(context.Context, []Invoice) error
// Please view Invoice to see what you are required to persist.
NewInvoice(context.Context, Invoice) error
}
Storage represents a user defined storage for storing invoices. All methods on Storage must be safe to call in any goroutine.
type Token ¶
type Token struct {
Symbol string `json:"symbol"`
Contract string `json:"contract"`
Decimals int64 `json:"decimals"`
}
Token represents a smart contract on a respective chain for example, USDT erc20
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
pricing_test
command
|
|
|
yurid
command
|
|
|
internal
|
|
|
solana
source: https://github.com/solana-foundation/solana-go/tree/main/base58 copied license: [LICENSE](./LICENSE)
|
source: https://github.com/solana-foundation/solana-go/tree/main/base58 copied license: [LICENSE](./LICENSE) |
|
solana/base58
source: https://github.com/solana-foundation/solana-go/tree/main/base58 copied license: [LICENSE](./LICENSE)
|
source: https://github.com/solana-foundation/solana-go/tree/main/base58 copied license: [LICENSE](./LICENSE) |