PathrockNetwork Gno Explorer
HomeBlocksTransactionsTokensRealmsPackagesValidatorsAnalytics

PathrockNetwork Gno Explorer — an independent explorer for Gno.land Mainnet (gnoland-1), operated by PathrockNetwork. Not an official Gno.land service.

gnowebarchive RPC

gno.land/r/gnoswap/router/v2

Realm
Open in gnoweb ↗

Overview

Kind
Realm (renderable)
Name
v2
Namespace
gnoswap / router
Files
21 (README)(gnomod.toml)
Exported functions
7
Module
gno.land/r/gnoswap/router/v2
gno
0.9

Files (21)

  • README.mdmarkdown
  • gnomod.tomltoml
  • assert.gnogno
  • base.gnogno
  • consts.gnogno
  • doc.gnogno
  • errors.gnogno
  • exact_in.gnogno
  • exact_out.gnogno
  • init.gnogno
  • instance.gnogno
  • protocol_fee_swap.gnogno
  • render.gnogno
  • router_dry.gnogno
  • router.gnogno
  • swap_callback.gnogno
  • swap_inner.gnogno
  • swap_multi.gnogno
  • swap_single.gnogno
  • type.gnogno
  • utils.gnogno
  • utils.gnogno
    1package router23import (4	"bytes"5	"errors"6	"strconv"7	"strings"8

    Functions

    • BuildSingleHopRoutePath(tokenA string, tokenB string, fee uint32) string

    • NewExactInParams(baseParams struct{InputToken string; OutputToken string; RouteArr string; QuoteArr string; SqrtPriceLimitX96 *gno.land/p/gnoswap/uint256/v1.Uint; Deadline int64}, amountIn int64, amountOutMin int64) struct{BaseSwapParams gno.land/r/gnoswap/router/v2.BaseSwapParams; AmountIn int64; AmountOutMin int64}

    • NewExactInSwapOperation(r *gno.land/r/gnoswap/router/v2.routerV1, pp struct{BaseSwapParams gno.land/r/gnoswap/router/v2.BaseSwapParams; AmountIn int64; AmountOutMin int64}) *gno.land/r/gnoswap/router/v2.ExactInSwapOperation

    • NewExactOutParams(baseParams struct{InputToken string; OutputToken string; RouteArr string; QuoteArr string; SqrtPriceLimitX96 *gno.land/p/gnoswap/uint256/v1.Uint; Deadline int64}, amountOut int64, amountInMax int64) struct{BaseSwapParams gno.land/r/gnoswap/router/v2.BaseSwapParams; AmountOut int64; AmountInMax int64}

    • NewExactOutSwapOperation(r *gno.land/r/gnoswap/router/v2.routerV1, pp struct{BaseSwapParams gno.land/r/gnoswap/router/v2.BaseSwapParams; AmountOut int64; AmountInMax int64}) *gno.land/r/gnoswap/router/v2.ExactOutSwapOperation

    • NewRouteParser() *gno.land/r/gnoswap/router/v2.RouteParser

    • NewRouterV1(routerStore interface {GetPendingProtocolFee func(string) int64; GetPendingProtocolFees func() map[string]int64; GetSwapFee func() uint64; HasPendingProtocolFeesKey func() bool; HasSwapFeeKey func() bool; RemovePendingProtocolFee func(int, .uverse.realm, string) .uverse.error; SetPendingProtocolFee func(int, .uverse.realm, string, int64) .uverse.error; SetPendingProtocolFees func(int, .uverse.realm, map[string]int64) .uverse.error; SetSwapFee func(int, .uverse.realm, uint64) .uverse.error}) interface {DrySwapRoute func(string, string, string, string, string, string, string) (string, string, .uverse.error); ExactInSingleSwapRoute func(int, .uverse.realm, string, string, string, string, string, string, int64, string) (string, string); ExactInSwapRoute func(int, .uverse.realm, string, string, string, string, string, string, int64, string) (string, string); ExactOutSingleSwapRoute func(int, .uverse.realm, string, string, string, string, string, string, int64, string) (string, string); ExactOutSwapRoute func(int, .uverse.realm, string, string, string, string, string, string, int64, string) (string, string); GetPendingProtocolFees func() map[string]int64; GetSwapFee func() uint64; Render func(string) string; SetSwapFee func(int, .uverse.realm, uint64); SwapCallback func(int, .uverse.realm, string, string, int64, int64, .uverse.address) .uverse.error}

    Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.

    Rendered

    RenderedRawgnoweb ↗
    This realm does not provide renderable output (vm/qrender → NoRenderDeclError). View on gnoweb ↗

    vm/qrender output, sanitized (docs/render-security.md) and displayed in an empty-sandbox iframe — scripts, forms and popups cannot run. Links stay inert in-preview; right-click to open.

    9 gnsmath "gno.land/p/gnoswap/gnsmath/v1"
    10 "gno.land/p/gnoswap/utils/v1"
    11 ufmt "gno.land/p/nt/ufmt/v0"
    12 "gno.land/r/gnoswap/pool"
    13
    14 i256 "gno.land/p/gnoswap/int256/v1"
    15 u256 "gno.land/p/gnoswap/uint256/v1"
    16)
    17
    18// calculateSwapAmountByQuote calculates swap amount based on quote percentage.
    19func calculateSwapAmountByQuote(amountSpecified int64, quote string) (int64, error) {
    20 quoteInt, err := strconv.ParseInt(quote, 10, 64)
    21 if err != nil {
    22 return 0, ufmt.Errorf("invalid quote(%s)", quote)
    23 }
    24
    25 if quoteInt < MinQuotePercentage || quoteInt > MaxQuotePercentage {
    26 return 0, ufmt.Errorf(ErrInvalidQuoteRange, quoteInt, MinQuotePercentage, MaxQuotePercentage)
    27 }
    28
    29 toSwap := gnsmath.SafeMulDivInt64(amountSpecified, quoteInt, PERCENTAGE_DENOMINATOR)
    30 if toSwap == 0 {
    31 return 0, errors.New(errInvalidSwapAmount)
    32 }
    33
    34 return toSwap, nil
    35}
    36
    37// assertHopsInRange ensures the number of hops is within the valid range of 1-3.
    38func assertHopsInRange(hops int) {
    39 switch hops {
    40 case 1, 2, 3:
    41 return
    42 default:
    43 panic(errors.New(errHopsOutOfRange))
    44 }
    45}
    46
    47// getDataForSinglePath extracts token addresses and fee from a single pool path.
    48//
    49// IMPORTANT: This function returns tokens in the order they appear in the route string,
    50// which represents the swap direction (tokenIn:tokenOut:fee), NOT the canonical pool ordering.
    51func getDataForSinglePath(poolPath string) (token0, token1 string, fee uint32) {
    52 token0, token1, fee, err := getDataForSinglePathWithError(poolPath)
    53 if err != nil {
    54 panic(err)
    55 }
    56
    57 return token0, token1, fee
    58}
    59
    60// getDataForSinglePathWithError extracts token addresses and fee from a single pool path with error handling.
    61func getDataForSinglePathWithError(poolPath string) (string, string, uint32, error) {
    62 poolPathSplit := strings.Split(poolPath, ":")
    63 if len(poolPathSplit) != 3 {
    64 return "", "", 0, makeErrorWithDetails(
    65 errInvalidPoolPath,
    66 ufmt.Sprintf("len(poolPathSplit) != 3, poolPath: %s", poolPath),
    67 )
    68 }
    69
    70 poolPathSplit[0] = strings.TrimSpace(poolPathSplit[0])
    71 poolPathSplit[1] = strings.TrimSpace(poolPathSplit[1])
    72
    73 if poolPathSplit[0] == "" || poolPathSplit[1] == "" {
    74 return "", "", 0, makeErrorWithDetails(
    75 errInvalidPoolPath,
    76 ufmt.Sprintf("token addresses cannot be empty: %s", poolPath),
    77 )
    78 }
    79
    80 f, err := strconv.Atoi(poolPathSplit[2])
    81 if err != nil {
    82 return "", "", 0, makeErrorWithDetails(
    83 errInvalidPoolPath,
    84 ufmt.Sprintf("invalid fee: %s", poolPathSplit[2]),
    85 )
    86 }
    87
    88 return poolPathSplit[0], poolPathSplit[1], uint32(f), nil
    89}
    90
    91// getDataForMultiPath extracts token addresses and fee from a multi-hop path at specified index.
    92func getDataForMultiPath(possiblePath string, poolIdx int) (token0, token1 string, fee uint32) {
    93 pools := strings.Split(possiblePath, POOL_SEPARATOR)
    94
    95 switch poolIdx {
    96 case 0:
    97 return getDataForSinglePath(pools[0])
    98 case 1:
    99 return getDataForSinglePath(pools[1])
    100 case 2:
    101 return getDataForSinglePath(pools[2])
    102 default:
    103 return "", "", uint32(0)
    104 }
    105}
    106
    107// i256MinMax returns the absolute values of x and y in min-max order.
    108func i256MinMax(x, y *i256.Int) (min, max *u256.Uint) {
    109 if x.Lt(y) || x.Eq(y) {
    110 return x.Abs(), y.Abs()
    111 }
    112 return y.Abs(), x.Abs()
    113}
    114
    115// validateRoutePaths validates multiple route paths to ensure they all start with inputToken and end with outputToken.
    116// This function processes comma-separated route paths and validates each path individually.
    117//
    118// Validates:
    119// - Each route path starts with the specified inputToken
    120// - Each route path ends with the specified outputToken
    121// - Route path format consistency (prevents swap-direction vs alphabetical pool ordering confusion)
    122//
    123// Parameters:
    124// - routePathArrString: comma-separated route paths (e.g., "gno.land/r/gnoland/wugnot.wugnot:gno.land/r/gnoswap/gns.GNS:500,gno.land/r/gnoland/wugnot.wugnot:gno.land/r/gnoswap/gns.GNS:3000")
    125// - inputToken: expected first token in all route paths
    126// - outputToken: expected last token in all route paths
    127//
    128// Examples:
    129// - Single route: "tokenA:tokenB:500" with inputToken="tokenA", outputToken="tokenB"
    130// - Multi-route: "tokenA:tokenB:500,tokenA:tokenC:3000*POOL*tokenC:tokenB:500" with inputToken="tokenA", outputToken="tokenB"
    131//
    132// Returns error if any route path validation fails.
    133func validateRoutePaths(routePathArrString, inputToken, outputToken string) error {
    134 routePaths := strings.Split(routePathArrString, ",")
    135
    136 for _, routePath := range routePaths {
    137 if err := validateRoutePath(routePath, inputToken, outputToken); err != nil {
    138 return err
    139 }
    140 }
    141
    142 return nil
    143}
    144
    145// validateRoutePath validates a single route path to ensure it starts with inputToken and ends with outputToken.
    146// This function handles both single-hop and multi-hop route paths.
    147//
    148// Validates:
    149// - Route path starts with the specified inputToken
    150// - Route path ends with the specified outputToken
    151// - Proper token ordering in swap direction (not alphabetical pool ordering)
    152//
    153// Route Path Formats:
    154// - single-hop: "tokenA:tokenB:fee" (direct swap between two tokens)
    155// - multi-hop: "tokenA:tokenB:fee1*POOL*tokenB:tokenC:fee2" (swap through intermediate tokens)
    156//
    157// Parameters:
    158// - routePath: single route path string (e.g., "gno.land/r/gnoland/wugnot.wugnot:gno.land/r/gnoswap/gns.GNS:500")
    159// - inputToken: expected first token in the route path
    160// - outputToken: expected last token in the route path
    161//
    162// Examples:
    163// - single-hop: "tokenA:tokenB:500" with inputToken="tokenA", outputToken="tokenB"
    164// - multi-hop: "tokenA:tokenB:3000*POOL*tokenB:tokenC:500" with inputToken="tokenA", outputToken="tokenC"
    165//
    166// Returns error with specific details if validation fails.
    167func validateRoutePath(routePath, inputToken, outputToken string) error {
    168 // Extract first and last tokens from the routePath
    169 var (
    170 firstToken, lastToken string
    171 err error
    172 )
    173
    174 // multi-hop routePath
    175 if strings.Contains(routePath, POOL_SEPARATOR) {
    176 pools := strings.Split(routePath, POOL_SEPARATOR)
    177
    178 // Get first token from first pool
    179 firstPool := pools[0]
    180 firstToken, _, _, err = getDataForSinglePathWithError(firstPool)
    181 if err != nil {
    182 return err
    183 }
    184
    185 // Validate hop-to-hop continuity: output token of hop N must equal input token of hop N+1
    186 err = validatePoolPathHopContinuity(pools)
    187 if err != nil {
    188 return err
    189 }
    190
    191 // Get last token from last pool
    192 lastPool := pools[len(pools)-1]
    193 _, lastToken, _, err = getDataForSinglePathWithError(lastPool)
    194 if err != nil {
    195 return err
    196 }
    197 } else {
    198 // single-hop routePath
    199 firstToken, lastToken, _, err = getDataForSinglePathWithError(routePath)
    200 if err != nil {
    201 return err
    202 }
    203 }
    204
    205 if firstToken == "" || lastToken == "" {
    206 return makeErrorWithDetails(errInvalidRoutePath, ufmt.Sprintf("firstToken: %s, lastToken: %s", firstToken, lastToken))
    207 }
    208
    209 // Validate consistency
    210 if firstToken != inputToken {
    211 return makeErrorWithDetails(errInvalidRouteFirstToken, ufmt.Sprintf("firstToken: %s, inputToken: %s", firstToken, inputToken))
    212 }
    213
    214 if lastToken != outputToken {
    215 return makeErrorWithDetails(errInvalidRouteLastToken, ufmt.Sprintf("lastToken: %s, outputToken: %s", lastToken, outputToken))
    216 }
    217
    218 return nil
    219}
    220
    221func validatePoolPathHopContinuity(routePoolPaths []string) error {
    222 if len(routePoolPaths) < 2 {
    223 return nil
    224 }
    225
    226 _, previousOut, _, pErr := getDataForSinglePathWithError(routePoolPaths[0])
    227 if pErr != nil {
    228 return pErr
    229 }
    230
    231 for i := 1; i < len(routePoolPaths); i++ {
    232 nextIn, nextOut, _, nErr := getDataForSinglePathWithError(routePoolPaths[i])
    233 if nErr != nil {
    234 return nErr
    235 }
    236
    237 if previousOut != nextIn {
    238 return makeErrorWithDetails(
    239 errRouteHopDisconnected,
    240 ufmt.Sprintf("hop %d output(%s) != hop %d input(%s)", i-1, previousOut, i, nextIn),
    241 )
    242 }
    243
    244 previousOut = nextOut
    245 }
    246
    247 return nil
    248}
    249
    250// splitSingleChar splits a string by a single character separator.
    251// This function is optimized for splitting strings with a single-byte separator
    252// and is more memory efficient than strings.Split for this use case.
    253func splitSingleChar(s string, sep byte) []string {
    254 if s == "" {
    255 return []string{""}
    256 }
    257
    258 result := make([]string, 0, bytes.Count([]byte(s), []byte{sep})+1)
    259 start := 0
    260 for i := range s {
    261 if s[i] == sep {
    262 result = append(result, s[start:i])
    263 start = i + 1
    264 }
    265 }
    266 result = append(result, s[start:])
    267 return result
    268}
    269
    270// parsePoolPathsByRoutePathArr parses route path array string and returns a slice of pool paths.
    271// This function converts route paths (which maintain swap direction) into canonical pool paths
    272// (which use alphabetical token ordering).
    273//
    274// The function processes comma-separated route paths and extracts individual pool information
    275// from each route, ensuring tokens are ordered alphabetically for consistent pool identification.
    276//
    277// Parameters:
    278// - routePathArr: comma-separated route paths string containing single or multi-hop routes
    279// Format examples:
    280// - Single route: "tokenA:tokenB:500"
    281// - Multiple routes: "tokenA:tokenB:500,tokenC:tokenD:3000"
    282// - Multi-hop routes: "tokenA:tokenB:500*POOL*tokenB:tokenC:3000"
    283//
    284// Returns:
    285// - []string: slice of canonical pool paths with alphabetically ordered tokens
    286// - error: parsing error if any route path is invalid
    287//
    288// Example:
    289//
    290// input: "gno.land/r/gnoland/wugnot.wugnot:gno.land/r/gnoswap/gns.GNS:500,gno.land/r/gnoland/wugnot.wugnot:gno.land/r/gnoswap/gns.GNS:3000"
    291// output: ["gno.land/r/gnoswap/gns.GNS:gno.land/r/gnoland/wugnot.wugnot:500", "gno.land/r/gnoswap/gns.GNS:gno.land/r/gnoland/wugnot.wugnot:3000"]
    292func parsePoolPathsByRoutePathArr(routePathArr string) ([]string, error) {
    293 poolPaths := make([]string, 0)
    294
    295 for _, routePaths := range strings.Split(routePathArr, ",") {
    296 for _, routePath := range strings.Split(routePaths, POOL_SEPARATOR) {
    297 token0Path, token1Path, fee, err := getDataForSinglePathWithError(routePath)
    298 if err != nil {
    299 return []string{}, err
    300 }
    301
    302 if token0Path > token1Path {
    303 token0Path, token1Path = token1Path, token0Path
    304 }
    305
    306 poolPath := pool.GetPoolPath(token0Path, token1Path, fee)
    307 poolPaths = append(poolPaths, poolPath)
    308 }
    309 }
    310
    311 return poolPaths, nil
    312}
    313
    314// BuildSingleHopRoutePath formats one route-direction single-hop path.
    315//
    316// Parameters:
    317// - tokenA: input token contract path
    318// - tokenB: output token contract path
    319// - fee: pool fee tier appended as a decimal value
    320//
    321// Returns:
    322// - routePath: tokenA:tokenB:fee formatted route string; panics for empty or identical tokens
    323func BuildSingleHopRoutePath(tokenA, tokenB string, fee uint32) string {
    324 if tokenA == "" || tokenB == "" {
    325 panic("token addresses cannot be empty")
    326 }
    327 if tokenA == tokenB {
    328 panic("tokenA and tokenB cannot be the same")
    329 }
    330
    331 return tokenA + ":" + tokenB + ":" + utils.FormatUint(fee)
    332}
    333