Skip to content

Commit

Permalink
feat: Bid pricing based on USD scale (#1385)
Browse files Browse the repository at this point in the history
* Pricing in USD in Bidding Engine
  • Loading branch information
arijitAD authored Oct 1, 2021
1 parent 65eae18 commit cbd9452
Show file tree
Hide file tree
Showing 2 changed files with 136 additions and 2 deletions.
39 changes: 37 additions & 2 deletions provider/bidengine/pricing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,26 @@ import (
"context"
"encoding/json"
"fmt"
"github.com/shopspring/decimal"
io "io"
"math"
"math/big"
"net/http"
"net/http/httptest"
"os"
"os/exec"
"path"
"path/filepath"
"testing"
"time"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/shopspring/decimal"
"github.com/stretchr/testify/require"

"github.com/ovrclk/akash/testutil"
atypes "github.com/ovrclk/akash/types"
"github.com/ovrclk/akash/types/unit"
dtypes "github.com/ovrclk/akash/x/deployment/types"
"github.com/stretchr/testify/require"
)

func Test_ScalePricingRejectsAllZero(t *testing.T) {
Expand Down Expand Up @@ -545,6 +549,37 @@ func Test_ScriptPricingWritesJsonToStdin(t *testing.T) {
}
}

func Test_ScriptPricingFromScript(t *testing.T) {
const (
mockAPIResponse = `{"akash-network":{"usd":3.57}}`
expectedPrice = 67843138
)

server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, err := w.Write([]byte(mockAPIResponse))
require.NoError(t, err)
}))
defer server.Close()

err := os.Setenv("API_URL", server.URL)
require.NoError(t, err)

scriptPath, err := filepath.Abs("../../script/usd_pricing_oracle.sh")
require.NoError(t, err)

pricing, err := MakeShellScriptPricing(scriptPath, 1, 30000*time.Millisecond)
require.NoError(t, err)
require.NotNil(t, pricing)

gspec := defaultGroupSpec()
gspec.Resources[0].Resources.Endpoints = make([]atypes.Endpoint, 7)

price, err := pricing.CalculatePrice(context.Background(), testutil.AccAddress(t).String(),
gspec)
require.NoError(t, err)
require.True(t, price.Equal(sdk.NewCoin("uakt", sdk.NewInt(expectedPrice))))
}

func TestRationalToIntConversion(t *testing.T) {
x := ceilBigRatToBigInt(big.NewRat(0, 1))
require.Equal(t, big.NewInt(0), x)
Expand Down
99 changes: 99 additions & 0 deletions script/usd_pricing_oracle.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#!/bin/bash

# To run this script, the following commands need to be installed:
#
# * jq
# * bc
# * curl

# One can set API_URL env variable to the url that returns coingecko like response, something like: `{"akash-network":{"usd":3.57}}`
# and if the API_URL isn't set, the default api url will be used

# URL to get current USD price per AKT
DEFAULT_API_URL="https://api.coingecko.com/api/v3/simple/price?ids=akash-network&vs_currencies=usd"

# These are the variables one can modify to change the USD scale for each resource kind
CPU_USD_SCALE=0.10
MEMORY_USD_SCALE=0.02
STORAGE_USD_SCALE=0.01
ENDPOINT_USD_SCALE=0.02

# used later for validation
MAX_INT64=9223372036854775807

# local variables used for calculation
memory_total=0
cpu_total=0
storage_total=0
endpoint_total=0

# read the JSON in `stdin` into $script_input
read -r script_input

# iterate over all the groups and calculate total quantity of each resource
for group in $(jq -c '.[]' <<<"$script_input"); do
count=$(jq '.count' <<<"$group")

memory_quantity=$(jq '.memory' <<<"$group")
memory_quantity=$((memory_quantity * count))
memory_total=$((memory_total + memory_quantity))

cpu_quantity=$(jq '.cpu' <<<"$group")
cpu_quantity=$((cpu_quantity * count))
cpu_total=$((cpu_total + cpu_quantity))

storage_quantity=$(jq '.storage' <<<"$group")
storage_quantity=$((storage_quantity * count))
storage_total=$((storage_total + storage_quantity))

endpoint_quantity=$(jq '.endpoint_quantity' <<<"$group")
endpoint_quantity=$((endpoint_quantity * count))
endpoint_total=$((endpoint_total + endpoint_quantity))
done

# calculate the total cost in USD for each resource
cpu_cost_usd=$(bc -l <<<"${cpu_total}*${CPU_USD_SCALE}")
memory_cost_usd=$(bc -l <<<"${memory_total}*${MEMORY_USD_SCALE}")
storage_cost_usd=$(bc -l <<<"${storage_total}*${STORAGE_USD_SCALE}")
endpoint_cost_usd=$(bc -l <<<"${endpoint_total}*${ENDPOINT_USD_SCALE}")

# validate the USD cost for each resource
if [ 1 -eq "$(bc <<<"${cpu_cost_usd}<0")" ] || [ 0 -eq "$(bc <<<"${cpu_cost_usd}<=${MAX_INT64}")" ] ||
[ 1 -eq "$(bc <<<"${memory_cost_usd}<0")" ] || [ 0 -eq "$(bc <<<"${memory_cost_usd}<=${MAX_INT64}")" ] ||
[ 1 -eq "$(bc <<<"${storage_cost_usd}<0")" ] || [ 0 -eq "$(bc <<<"${storage_cost_usd}<=${MAX_INT64}")" ] ||
[ 1 -eq "$(bc <<<"${endpoint_cost_usd}<0")" ] || [ 0 -eq "$(bc <<<"${endpoint_cost_usd}<=${MAX_INT64}")" ]; then
exit 1
fi

# finally, calculate the total cost in USD of all resources and validate it
total_cost_usd=$(bc -l <<<"${cpu_cost_usd}+${memory_cost_usd}+${storage_cost_usd}+${endpoint_cost_usd}")
if [ 1 -eq "$(bc <<<"${total_cost_usd}<0")" ] || [ 0 -eq "$(bc <<<"${total_cost_usd}<=${MAX_INT64}")" ]; then
exit 1
fi

# call the API and find out the current USD price per AKT
if [ -z "$API_URL" ]; then
API_URL=$DEFAULT_API_URL
fi

API_RESPONSE=$(curl -s "$API_URL")
curl_exit_status=$?
if [ $curl_exit_status != 0 ]; then
exit $curl_exit_status
fi
usd_per_akt=$(jq '."akash-network"."usd"' <<<"$API_RESPONSE")

# validate the current USD price per AKT is not zero
if [ 1 -eq "$(bc <<< "${usd_per_akt}==0")" ]; then
exit 1
fi

# calculate the total cost in uAKT
total_cost_akt=$(bc -l <<<"${total_cost_usd}/${usd_per_akt}")
total_cost_uakt=$(bc -l <<<"${total_cost_akt}*1000000")

# Round upwards to get an integer
total_cost_uakt=$(echo "$total_cost_uakt" | jq '.|ceil')

# return the price in uAKT
echo "$total_cost_uakt"

0 comments on commit cbd9452

Please sign in to comment.